/*
 -- ============================================================================
 -- FILE NAME	: decoder.v
 -- DESCRIPTION : ½ßfR[_
 -- ----------------------------------------------------------------------------
 -- Revision  Date		  Coding_by	 Comment
 -- 1.0.0	  2011/06/27  suito		 VKì¬
 -- ============================================================================
*/

/********** ¤Êwb_t@C **********/
`include "nettype.h"
`include "global_config.h"
`include "stddef.h"

/********** ÂÊwb_t@C **********/
`include "isa.h"
`include "cpu.h"

/********** W[ **********/
module decoder (
	/********** IF/IDpCvCWX^ **********/
	input  wire [`WordAddrBus]	 if_pc,			 // vOJE^
	input  wire [`WordDataBus]	 if_insn,		 // ½ß
	input  wire					 if_en,			 // pCvCf[^ÌLø
	/********** GPRC^tF[X **********/
	input  wire [`WordDataBus]	 gpr_rd_data_0, // ÇÝoµf[^ 0
	input  wire [`WordDataBus]	 gpr_rd_data_1, // ÇÝoµf[^ 1
	output wire [`RegAddrBus]	 gpr_rd_addr_0, // ÇÝoµAhX 0
	output wire [`RegAddrBus]	 gpr_rd_addr_1, // ÇÝoµAhX 1
	/********** tH[fBO **********/
	// IDXe[W©çÌtH[fBO
	input  wire					 id_en,			// pCvCf[^ÌLø
	input  wire [`RegAddrBus]	 id_dst_addr,	// «ÝAhX
	input  wire					 id_gpr_we_,	// «ÝLø
	input  wire [`MemOpBus]		 id_mem_op,		// Iy[V
	// EXXe[W©çÌtH[fBO
	input  wire					 ex_en,			// pCvCf[^ÌLø
	input  wire [`RegAddrBus]	 ex_dst_addr,	// «ÝAhX
	input  wire					 ex_gpr_we_,	// «ÝLø
	input  wire [`WordDataBus]	 ex_fwd_data,	// tH[fBOf[^
	// MEMXe[W©çÌtH[fBO
	input  wire [`WordDataBus]	 mem_fwd_data,	// tH[fBOf[^
	/********** §äWX^C^tF[X **********/
	input  wire [`CpuExeModeBus] exe_mode,		// Às[h
	input  wire [`WordDataBus]	 creg_rd_data,	// ÇÝoµf[^
	output wire [`RegAddrBus]	 creg_rd_addr,	// ÇÝoµAhX
	/********** fR[hÊ **********/
	output reg	[`AluOpBus]		 alu_op,		// ALUIy[V
	output reg	[`WordDataBus]	 alu_in_0,		// ALUüÍ 0
	output reg	[`WordDataBus]	 alu_in_1,		// ALUüÍ 1
	output reg	[`WordAddrBus]	 br_addr,		// ªòAhX
	output reg					 br_taken,		// ªòÌ¬§
	output reg					 br_flag,		// ªòtO
	output reg	[`MemOpBus]		 mem_op,		// Iy[V
	output wire [`WordDataBus]	 mem_wr_data,	// «Ýf[^
	output reg	[`CtrlOpBus]	 ctrl_op,		// §äIy[V
	output reg	[`RegAddrBus]	 dst_addr,		// ÄpWX^«ÝAhX
	output reg					 gpr_we_,		// ÄpWX^«ÝLø
	output reg	[`IsaExpBus]	 exp_code,		// áOR[h
	output reg					 ld_hazard		// [hnU[h
);

	/********** ½ßtB[h **********/
	wire [`IsaOpBus]	op		= if_insn[`IsaOpLoc];	  // IyR[h
	wire [`RegAddrBus]	ra_addr = if_insn[`IsaRaAddrLoc]; // RaAhX
	wire [`RegAddrBus]	rb_addr = if_insn[`IsaRbAddrLoc]; // RbAhX
	wire [`RegAddrBus]	rc_addr = if_insn[`IsaRcAddrLoc]; // RcAhX
	wire [`IsaImmBus]	imm		= if_insn[`IsaImmLoc];	  // ¦l
	/********** ¦l **********/
	// g£
	wire [`WordDataBus] imm_s = {{`ISA_EXT_W{imm[`ISA_IMM_MSB]}}, imm};
	// [g£
	wire [`WordDataBus] imm_u = {{`ISA_EXT_W{1'b0}}, imm};
	/********** WX^ÌÇÝoµAhX **********/
	assign gpr_rd_addr_0 = ra_addr; // ÄpWX^ÇÝoµAhX 0
	assign gpr_rd_addr_1 = rb_addr; // ÄpWX^ÇÝoµAhX 1
	assign creg_rd_addr	 = ra_addr; // §äWX^ÇÝoµAhX
	/********** ÄpWX^ÌÇÝoµf[^ **********/
	reg			[`WordDataBus]	ra_data;						  // ÈµRa
	wire signed [`WordDataBus]	s_ra_data = $signed(ra_data);	  // t«Ra
	reg			[`WordDataBus]	rb_data;						  // ÈµRb
	wire signed [`WordDataBus]	s_rb_data = $signed(rb_data);	  // t«Rb
	assign mem_wr_data = rb_data; // «Ýf[^
	/********** AhX **********/
	wire [`WordAddrBus] ret_addr  = if_pc + 1'b1;					 // ßèÔn
	wire [`WordAddrBus] br_target = if_pc + imm_s[`WORD_ADDR_MSB:0]; // ªòæ
	wire [`WordAddrBus] jr_target = ra_data[`WordAddrLoc];		   // Wvæ

	/********** tH[fBO **********/
	always @(*) begin
		/* RaWX^ */
		if ((id_en == `ENABLE) && (id_gpr_we_ == `ENABLE_) && 
			(id_dst_addr == ra_addr)) begin
			ra_data = ex_fwd_data;	 // EXXe[W©çÌtH[fBO
		end else if ((ex_en == `ENABLE) && (ex_gpr_we_ == `ENABLE_) && 
					 (ex_dst_addr == ra_addr)) begin
			ra_data = mem_fwd_data;	 // MEMXe[W©çÌtH[fBO
		end else begin
			ra_data = gpr_rd_data_0; // WX^t@C©çÌÇÝoµ
		end
		/* RbWX^ */
		if ((id_en == `ENABLE) && (id_gpr_we_ == `ENABLE_) && 
			(id_dst_addr == rb_addr)) begin
			rb_data = ex_fwd_data;	 // EXXe[W©çÌtH[fBO
		end else if ((ex_en == `ENABLE) && (ex_gpr_we_ == `ENABLE_) && 
					 (ex_dst_addr == rb_addr)) begin
			rb_data = mem_fwd_data;	 // MEMXe[W©çÌtH[fBO
		end else begin
			rb_data = gpr_rd_data_1; // WX^t@C©çÌÇÝoµ
		end
	end

	/********** [hnU[hÌo **********/
	always @(*) begin
		if ((id_en == `ENABLE) && (id_mem_op == `MEM_OP_LDW) &&
			((id_dst_addr == ra_addr) || (id_dst_addr == rb_addr))) begin
			ld_hazard = `ENABLE;  // [hnU[h
		end else begin
			ld_hazard = `DISABLE; // nU[hÈµ
		end
	end

	/********** ½ßÌfR[h **********/
	always @(*) begin
		/* ftHgl */
		alu_op	 = `ALU_OP_NOP;
		alu_in_0 = ra_data;
		alu_in_1 = rb_data;
		br_taken = `DISABLE;
		br_flag	 = `DISABLE;
		br_addr	 = {`WORD_ADDR_W{1'b0}};
		mem_op	 = `MEM_OP_NOP;
		ctrl_op	 = `CTRL_OP_NOP;
		dst_addr = rb_addr;
		gpr_we_	 = `DISABLE_;
		exp_code = `ISA_EXP_NO_EXP;
		/* IyR[hÌ»è */
		if (if_en == `ENABLE) begin
			case (op)
				/* _Z½ß */
				`ISA_OP_ANDR  : begin // WX^¯mÌ_Ï
					alu_op	 = `ALU_OP_AND;
					dst_addr = rc_addr;
					gpr_we_	 = `ENABLE_;
				end
				`ISA_OP_ANDI  : begin // WX^Æ¦lÌ_Ï
					alu_op	 = `ALU_OP_AND;
					alu_in_1 = imm_u;
					gpr_we_	 = `ENABLE_;
				end
				`ISA_OP_ORR	  : begin // WX^¯mÌ_a
					alu_op	 = `ALU_OP_OR;
					dst_addr = rc_addr;
					gpr_we_	 = `ENABLE_;
				end
				`ISA_OP_ORI	  : begin // WX^Æ¦lÌ_a
					alu_op	 = `ALU_OP_OR;
					alu_in_1 = imm_u;
					gpr_we_	 = `ENABLE_;
				end
				`ISA_OP_XORR  : begin // WX^¯mÌr¼I_a
					alu_op	 = `ALU_OP_XOR;
					dst_addr = rc_addr;
					gpr_we_	 = `ENABLE_;
				end
				`ISA_OP_XORI  : begin // WX^Æ¦lÌr¼I_a
					alu_op	 = `ALU_OP_XOR;
					alu_in_1 = imm_u;
					gpr_we_	 = `ENABLE_;
				end
				/* ZpZ½ß */
				`ISA_OP_ADDSR : begin // WX^¯mÌt«ÁZ
					alu_op	 = `ALU_OP_ADDS;
					dst_addr = rc_addr;
					gpr_we_	 = `ENABLE_;
				end
				`ISA_OP_ADDSI : begin // WX^Æ¦lÌt«ÁZ
					alu_op	 = `ALU_OP_ADDS;
					alu_in_1 = imm_s;
					gpr_we_	 = `ENABLE_;
				end
				`ISA_OP_ADDUR : begin // WX^¯mÌÈµÁZ
					alu_op	 = `ALU_OP_ADDU;
					dst_addr = rc_addr;
					gpr_we_	 = `ENABLE_;
				end
				`ISA_OP_ADDUI : begin // WX^Æ¦lÌÈµÁZ
					alu_op	 = `ALU_OP_ADDU;
					alu_in_1 = imm_s;
					gpr_we_	 = `ENABLE_;
				end
				`ISA_OP_SUBSR : begin // WX^¯mÌt«¸Z
					alu_op	 = `ALU_OP_SUBS;
					dst_addr = rc_addr;
					gpr_we_	 = `ENABLE_;
				end
				`ISA_OP_SUBUR : begin // WX^¯mÌÈµ¸Z
					alu_op	 = `ALU_OP_SUBU;
					dst_addr = rc_addr;
					gpr_we_	 = `ENABLE_;
				end
				/* Vtg½ß */
				`ISA_OP_SHRLR : begin // WX^¯mÌ_EVtg
					alu_op	 = `ALU_OP_SHRL;
					dst_addr = rc_addr;
					gpr_we_	 = `ENABLE_;
				end
				`ISA_OP_SHRLI : begin // WX^Æ¦lÌ_EVtg
					alu_op	 = `ALU_OP_SHRL;
					alu_in_1 = imm_u;
					gpr_we_	 = `ENABLE_;
				end
				`ISA_OP_SHLLR : begin // WX^¯mÌ_¶Vtg
					alu_op	 = `ALU_OP_SHLL;
					dst_addr = rc_addr;
					gpr_we_	 = `ENABLE_;
				end
				`ISA_OP_SHLLI : begin // WX^Æ¦lÌ_¶Vtg
					alu_op	 = `ALU_OP_SHLL;
					alu_in_1 = imm_u;
					gpr_we_	 = `ENABLE_;
				end
				/* ªò½ß */
				`ISA_OP_BE	  : begin // WX^¯mÌt«äriRa == Rbj
					br_addr	 = br_target;
					br_taken = (ra_data == rb_data) ? `ENABLE : `DISABLE;
					br_flag	 = `ENABLE;
				end
				`ISA_OP_BNE	  : begin // WX^¯mÌt«äriRa != Rbj
					br_addr	 = br_target;
					br_taken = (ra_data != rb_data) ? `ENABLE : `DISABLE;
					br_flag	 = `ENABLE;
				end
				`ISA_OP_BSGT  : begin // WX^¯mÌt«äriRa < Rbj
					br_addr	 = br_target;
					br_taken = (s_ra_data < s_rb_data) ? `ENABLE : `DISABLE;
					br_flag	 = `ENABLE;
				end
				`ISA_OP_BUGT  : begin // WX^¯mÌÈµäriRa < Rbj
					br_addr	 = br_target;
					br_taken = (ra_data < rb_data) ? `ENABLE : `DISABLE;
					br_flag	 = `ENABLE;
				end
				`ISA_OP_JMP	  : begin // ³ðªò
					br_addr	 = jr_target;
					br_taken = `ENABLE;
					br_flag	 = `ENABLE;
				end
				`ISA_OP_CALL  : begin // R[
					alu_in_0 = {ret_addr, {`BYTE_OFFSET_W{1'b0}}};
					br_addr	 = jr_target;
					br_taken = `ENABLE;
					br_flag	 = `ENABLE;
					dst_addr = `REG_ADDR_W'd31;
					gpr_we_	 = `ENABLE_;
				end
				/* ANZX½ß */
				`ISA_OP_LDW	  : begin // [hÇÝoµ
					alu_op	 = `ALU_OP_ADDU;
					alu_in_1 = imm_s;
					mem_op	 = `MEM_OP_LDW;
					gpr_we_	 = `ENABLE_;
				end
				`ISA_OP_STW	  : begin // [h«Ý
					alu_op	 = `ALU_OP_ADDU;
					alu_in_1 = imm_s;
					mem_op	 = `MEM_OP_STW;
				end
				/* VXeR[½ß */
				`ISA_OP_TRAP  : begin // gbv
					exp_code = `ISA_EXP_TRAP;
				end
				/* Á ½ß */
				`ISA_OP_RDCR  : begin // §äWX^ÌÇÝoµ
					if (exe_mode == `CPU_KERNEL_MODE) begin
						alu_in_0 = creg_rd_data;
						gpr_we_	 = `ENABLE_;
					end else begin
						exp_code = `ISA_EXP_PRV_VIO;
					end
				end
				`ISA_OP_WRCR  : begin // §äWX^ÖÌ«Ý
					if (exe_mode == `CPU_KERNEL_MODE) begin
						ctrl_op	 = `CTRL_OP_WRCR;
					end else begin
						exp_code = `ISA_EXP_PRV_VIO;
					end
				end
				`ISA_OP_EXRT  : begin // áO©çÌA
					if (exe_mode == `CPU_KERNEL_MODE) begin
						ctrl_op	 = `CTRL_OP_EXRT;
					end else begin
						exp_code = `ISA_EXP_PRV_VIO;
					end
				end
				/* »Ì¼Ì½ß */
				default		  : begin // ¢è`½ß
					exp_code = `ISA_EXP_UNDEF_INSN;
				end
			endcase
		end
	end

endmodule
