module decoder (
	/********** IF/ID¥Ñ¥¤¥×¥é¥¤¥ó¥ì¥¸¥¹¥¿ **********/
	input  wire [`WordAddrBus]	 if_pc,			 // ¥×¥í¥°¥é¥à¥«¥¦¥ó¥¿
	input  wire [`WordDataBus]	 if_insn,		 // ÃüÁî
	input  wire					 if_en,			 // ¥Ñ¥¤¥×¥é¥¤¥ó¥Ç©`¥¿¤ÎÓÐ¿
	/********** GPR¥¤¥ó¥¿¥Õ¥§©`¥¹ **********/
	input  wire [`WordDataBus]	 gpr_rd_data_0, // Õi¤ß³ö¤·¥Ç©`¥¿ 0
	input  wire [`WordDataBus]	 gpr_rd_data_1, // Õi¤ß³ö¤·¥Ç©`¥¿ 1
	output wire [`RegAddrBus]	 gpr_rd_addr_0, // Õi¤ß³ö¤·¥¢¥É¥ì¥¹ 0
	output wire [`RegAddrBus]	 gpr_rd_addr_1, // Õi¤ß³ö¤·¥¢¥É¥ì¥¹ 1
	/********** ¥Õ¥©¥ï©`¥Ç¥£¥ó¥° **********/
	// ID¥¹¥Æ©`¥¸¤«¤é¤Î¥Õ¥©¥ï©`¥Ç¥£¥ó¥°
	input  wire					 id_en,			// ¥Ñ¥¤¥×¥é¥¤¥ó¥Ç©`¥¿¤ÎÓÐ¿
	input  wire [`RegAddrBus]	 id_dst_addr,	// ø¤­Þz¤ß¥¢¥É¥ì¥¹
	input  wire					 id_gpr_we_,	// ø¤­Þz¤ßÓÐ¿
	input  wire [`MemOpBus]		 id_mem_op,		// ¥á¥â¥ê¥ª¥Ú¥ì©`¥·¥ç¥ó
	// EX¥¹¥Æ©`¥¸¤«¤é¤Î¥Õ¥©¥ï©`¥Ç¥£¥ó¥°
	input  wire					 ex_en,			// ¥Ñ¥¤¥×¥é¥¤¥ó¥Ç©`¥¿¤ÎÓÐ¿
	input  wire [`RegAddrBus]	 ex_dst_addr,	// ø¤­Þz¤ß¥¢¥É¥ì¥¹
	input  wire					 ex_gpr_we_,	// ø¤­Þz¤ßÓÐ¿
	input  wire [`WordDataBus]	 ex_fwd_data,	// ¥Õ¥©¥ï©`¥Ç¥£¥ó¥°¥Ç©`¥¿
	// MEM¥¹¥Æ©`¥¸¤«¤é¤Î¥Õ¥©¥ï©`¥Ç¥£¥ó¥°
	input  wire [`WordDataBus]	 mem_fwd_data,	// ¥Õ¥©¥ï©`¥Ç¥£¥ó¥°¥Ç©`¥¿
	/********** ÖÆÓù¥ì¥¸¥¹¥¿¥¤¥ó¥¿¥Õ¥§©`¥¹ **********/
	input  wire [`CpuExeModeBus] exe_mode,		// gÐÐ¥â©`¥É
	input  wire [`WordDataBus]	 creg_rd_data,	// Õi¤ß³ö¤·¥Ç©`¥¿
	output wire [`RegAddrBus]	 creg_rd_addr,	// Õi¤ß³ö¤·¥¢¥É¥ì¥¹
	/********** ¥Ç¥³©`¥É½Y¹û **********/
	output reg	[`AluOpBus]		 alu_op,		// ALU¥ª¥Ú¥ì©`¥·¥ç¥ó
	output reg	[`WordDataBus]	 alu_in_0,		// ALUÈëÁ¦ 0
	output reg	[`WordDataBus]	 alu_in_1,		// ALUÈëÁ¦ 1
	output reg	[`WordAddrBus]	 br_addr,		// ·Öáª¥¢¥É¥ì¥¹
	output reg					 br_taken,		// ·Öáª¤Î³ÉÁ¢
	output reg					 br_flag,		// ·Öáª¥Õ¥é¥°
	output reg	[`MemOpBus]		 mem_op,		// ¥á¥â¥ê¥ª¥Ú¥ì©`¥·¥ç¥ó
	output wire [`WordDataBus]	 mem_wr_data,	// ¥á¥â¥êø¤­Þz¤ß¥Ç©`¥¿
	output reg	[`CtrlOpBus]	 ctrl_op,		// ÖÆÓù¥ª¥Ú¥ì©`¥·¥ç¥ó
	output reg	[`RegAddrBus]	 dst_addr,		// øÓÃ¥ì¥¸¥¹¥¿ø¤­Þz¤ß¥¢¥É¥ì¥¹
	output reg					 gpr_we_,		// øÓÃ¥ì¥¸¥¹¥¿ø¤­Þz¤ßÓÐ¿
	output reg	[`IsaExpBus]	 exp_code,		// ÀýÍâ¥³©`¥É
	output reg					 ld_hazard		// ¥í©`¥É¥Ï¥¶©`¥É
);

	/********** ÃüÁî¥Õ¥£©`¥ë¥É **********/
	wire [`IsaOpBus]	op		= if_insn[`IsaOpLoc];	  // ¥ª¥Ú¥³©`¥É
	wire [`RegAddrBus]	ra_addr = if_insn[`IsaRaAddrLoc]; // Ra¥¢¥É¥ì¥¹
	wire [`RegAddrBus]	rb_addr = if_insn[`IsaRbAddrLoc]; // Rb¥¢¥É¥ì¥¹
	wire [`RegAddrBus]	rc_addr = if_insn[`IsaRcAddrLoc]; // Rc¥¢¥É¥ì¥¹
	wire [`IsaImmBus]	imm		= if_insn[`IsaImmLoc];	  // ¼´
	/********** ¼´ **********/
	// ·ûºÅ
	wire [`WordDataBus] imm_s = {{`ISA_EXT_W{imm[`ISA_IMM_MSB]}}, imm};
	// ¥¼¥í
	wire [`WordDataBus] imm_u = {{`ISA_EXT_W{1'b0}}, imm};
	/********** ¥ì¥¸¥¹¥¿¤ÎÕi¤ß³ö¤·¥¢¥É¥ì¥¹ **********/
	assign gpr_rd_addr_0 = ra_addr; // øÓÃ¥ì¥¸¥¹¥¿Õi¤ß³ö¤·¥¢¥É¥ì¥¹ 0
	assign gpr_rd_addr_1 = rb_addr; // øÓÃ¥ì¥¸¥¹¥¿Õi¤ß³ö¤·¥¢¥É¥ì¥¹ 1
	assign creg_rd_addr	 = ra_addr; // ÖÆÓù¥ì¥¸¥¹¥¿Õi¤ß³ö¤·¥¢¥É¥ì¥¹
	/********** øÓÃ¥ì¥¸¥¹¥¿¤ÎÕi¤ß³ö¤·¥Ç©`¥¿ **********/
	reg			[`WordDataBus]	ra_data;						  // ·ûºÅ¤Ê¤·Ra
	wire signed [`WordDataBus]	s_ra_data = $signed(ra_data);	  // ·ûºÅ¸¶¤­Ra
	reg			[`WordDataBus]	rb_data;						  // ·ûºÅ¤Ê¤·Rb
	wire signed [`WordDataBus]	s_rb_data = $signed(rb_data);	  // ·ûºÅ¸¶¤­Rb
	assign mem_wr_data = rb_data; // ¥á¥â¥êø¤­Þz¤ß¥Ç©`¥¿
	/********** ¥¢¥É¥ì¥¹ **********/
	wire [`WordAddrBus] ret_addr  = if_pc + 1'b1;					 // ø¤ê·¬µØ
	wire [`WordAddrBus] br_target = if_pc + imm_s[`WORD_ADDR_MSB:0]; // ·ÖáªÏÈ
	wire [`WordAddrBus] jr_target = ra_data[`WordAddrLoc];		   // ¥¸¥ã¥ó¥×ÏÈ

	/********** ¥Õ¥©¥ï©`¥Ç¥£¥ó¥° **********/
	always @(*) begin
		/* Ra¥ì¥¸¥¹¥¿ */
		if ((id_en == `ENABLE) && (id_gpr_we_ == `ENABLE_) && 
			(id_dst_addr == ra_addr)) begin
			ra_data = ex_fwd_data;	 // EX¥¹¥Æ©`¥¸¤«¤é¤Î¥Õ¥©¥ï©`¥Ç¥£¥ó¥°
		end else if ((ex_en == `ENABLE) && (ex_gpr_we_ == `ENABLE_) && 
					 (ex_dst_addr == ra_addr)) begin
			ra_data = mem_fwd_data;	 // MEM¥¹¥Æ©`¥¸¤«¤é¤Î¥Õ¥©¥ï©`¥Ç¥£¥ó¥°
		end else begin
			ra_data = gpr_rd_data_0; // ¥ì¥¸¥¹¥¿¥Õ¥¡¥¤¥ë¤«¤é¤ÎÕi¤ß³ö¤·
		end
		/* Rb¥ì¥¸¥¹¥¿ */
		if ((id_en == `ENABLE) && (id_gpr_we_ == `ENABLE_) && 
			(id_dst_addr == rb_addr)) begin
			rb_data = ex_fwd_data;	 // EX¥¹¥Æ©`¥¸¤«¤é¤Î¥Õ¥©¥ï©`¥Ç¥£¥ó¥°
		end else if ((ex_en == `ENABLE) && (ex_gpr_we_ == `ENABLE_) && 
					 (ex_dst_addr == rb_addr)) begin
			rb_data = mem_fwd_data;	 // MEM¥¹¥Æ©`¥¸¤«¤é¤Î¥Õ¥©¥ï©`¥Ç¥£¥ó¥°
		end else begin
			rb_data = gpr_rd_data_1; // ¥ì¥¸¥¹¥¿¥Õ¥¡¥¤¥ë¤«¤é¤ÎÕi¤ß³ö¤·
		end
	end

	/********** ¥í©`¥É¥Ï¥¶©`¥É¤ÎÊ³ö **********/
	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;  // ¥í©`¥É¥Ï¥¶©`¥É
		end else begin
			ld_hazard = `DISABLE; // ¥Ï¥¶©`¥É¤Ê¤·
		end
	end

	/********** ÃüÁî¤Î¥Ç¥³©`¥É **********/
	always @(*) begin
		/* ¥Ç¥Õ¥©¥ë¥È */
		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;
		/* ¥ª¥Ú¥³©`¥É¤ÎÅÐ¶¨ */
		if (if_en == `ENABLE) begin
			case (op)
				/* ÕÀíÑÝËãÃüÁî */
				`ISA_OP_ANDR  : begin // ¥ì¥¸¥¹¥¿Í¬Ê¿¤ÎÕÀí·e
					alu_op	 = `ALU_OP_AND;
					dst_addr = rc_addr;
					gpr_we_	 = `ENABLE_;
				end
				`ISA_OP_ANDI  : begin // ¥ì¥¸¥¹¥¿¤È¼´¤ÎÕÀí·e
					alu_op	 = `ALU_OP_AND;
					alu_in_1 = imm_u;
					gpr_we_	 = `ENABLE_;
				end
				`ISA_OP_ORR	  : begin // ¥ì¥¸¥¹¥¿Í¬Ê¿¤ÎÕÀíºÍ
					alu_op	 = `ALU_OP_OR;
					dst_addr = rc_addr;
					gpr_we_	 = `ENABLE_;
				end
				`ISA_OP_ORI	  : begin // ¥ì¥¸¥¹¥¿¤È¼´¤ÎÕÀíºÍ
					alu_op	 = `ALU_OP_OR;
					alu_in_1 = imm_u;
					gpr_we_	 = `ENABLE_;
				end
				`ISA_OP_XORR  : begin // ¥ì¥¸¥¹¥¿Í¬Ê¿¤ÎÅÅËûµÄÕÀíºÍ
					alu_op	 = `ALU_OP_XOR;
					dst_addr = rc_addr;
					gpr_we_	 = `ENABLE_;
				end
				`ISA_OP_XORI  : begin // ¥ì¥¸¥¹¥¿¤È¼´¤ÎÅÅËûµÄÕÀíºÍ
					alu_op	 = `ALU_OP_XOR;
					alu_in_1 = imm_u;
					gpr_we_	 = `ENABLE_;
				end
				/* ËãÐgÑÝËãÃüÁî */
				`ISA_OP_ADDSR : begin // ¥ì¥¸¥¹¥¿Í¬Ê¿¤Î·ûºÅ¸¶¤­¼ÓËã
					alu_op	 = `ALU_OP_ADDS;
					dst_addr = rc_addr;
					gpr_we_	 = `ENABLE_;
				end
				`ISA_OP_ADDSI : begin // ¥ì¥¸¥¹¥¿¤È¼´¤Î·ûºÅ¸¶¤­¼ÓËã
					alu_op	 = `ALU_OP_ADDS;
					alu_in_1 = imm_s;
					gpr_we_	 = `ENABLE_;
				end
				`ISA_OP_ADDUR : begin // ¥ì¥¸¥¹¥¿Í¬Ê¿¤Î·ûºÅ¤Ê¤·¼ÓËã
					alu_op	 = `ALU_OP_ADDU;
					dst_addr = rc_addr;
					gpr_we_	 = `ENABLE_;
				end
				`ISA_OP_ADDUI : begin // ¥ì¥¸¥¹¥¿¤È¼´¤Î·ûºÅ¤Ê¤·¼ÓËã
					alu_op	 = `ALU_OP_ADDU;
					alu_in_1 = imm_s;
					gpr_we_	 = `ENABLE_;
				end
				`ISA_OP_SUBSR : begin // ¥ì¥¸¥¹¥¿Í¬Ê¿¤Î·ûºÅ¸¶¤­pËã
					alu_op	 = `ALU_OP_SUBS;
					dst_addr = rc_addr;
					gpr_we_	 = `ENABLE_;
				end
				`ISA_OP_SUBUR : begin // ¥ì¥¸¥¹¥¿Í¬Ê¿¤Î·ûºÅ¤Ê¤·pËã
					alu_op	 = `ALU_OP_SUBU;
					dst_addr = rc_addr;
					gpr_we_	 = `ENABLE_;
				end
				/* ¥·¥Õ¥ÈÃüÁî */
				`ISA_OP_SHRLR : begin // ¥ì¥¸¥¹¥¿Í¬Ê¿¤ÎÕÀíÓÒ¥·¥Õ¥È
					alu_op	 = `ALU_OP_SHRL;
					dst_addr = rc_addr;
					gpr_we_	 = `ENABLE_;
				end
				`ISA_OP_SHRLI : begin // ¥ì¥¸¥¹¥¿¤È¼´¤ÎÕÀíÓÒ¥·¥Õ¥È
					alu_op	 = `ALU_OP_SHRL;
					alu_in_1 = imm_u;
					gpr_we_	 = `ENABLE_;
				end
				`ISA_OP_SHLLR : begin // ¥ì¥¸¥¹¥¿Í¬Ê¿¤ÎÕÀí×ó¥·¥Õ¥È
					alu_op	 = `ALU_OP_SHLL;
					dst_addr = rc_addr;
					gpr_we_	 = `ENABLE_;
				end
				`ISA_OP_SHLLI : begin // ¥ì¥¸¥¹¥¿¤È¼´¤ÎÕÀí×ó¥·¥Õ¥È
					alu_op	 = `ALU_OP_SHLL;
					alu_in_1 = imm_u;
					gpr_we_	 = `ENABLE_;
				end
				/* ·ÖáªÃüÁî */
				`ISA_OP_BE	  : begin // ¥ì¥¸¥¹¥¿Í¬Ê¿¤Î·ûºÅ¸¶¤­±ÈÝ^£¨Ra == Rb£©
					br_addr	 = br_target;
					br_taken = (ra_data == rb_data) ? `ENABLE : `DISABLE;
					br_flag	 = `ENABLE;
				end
				`ISA_OP_BNE	  : begin // ¥ì¥¸¥¹¥¿Í¬Ê¿¤Î·ûºÅ¸¶¤­±ÈÝ^£¨Ra != Rb£©
					br_addr	 = br_target;
					br_taken = (ra_data != rb_data) ? `ENABLE : `DISABLE;
					br_flag	 = `ENABLE;
				end
				`ISA_OP_BSGT  : begin // ¥ì¥¸¥¹¥¿Í¬Ê¿¤Î·ûºÅ¸¶¤­±ÈÝ^£¨Ra < Rb£©
					br_addr	 = br_target;
					br_taken = (s_ra_data < s_rb_data) ? `ENABLE : `DISABLE;
					br_flag	 = `ENABLE;
				end
				`ISA_OP_BUGT  : begin // ¥ì¥¸¥¹¥¿Í¬Ê¿¤Î·ûºÅ¤Ê¤·±ÈÝ^£¨Ra < Rb£©
					br_addr	 = br_target;
					br_taken = (ra_data < rb_data) ? `ENABLE : `DISABLE;
					br_flag	 = `ENABLE;
				end
				`ISA_OP_JMP	  : begin // oÌõ¼þ·Öáª
					br_addr	 = jr_target;
					br_taken = `ENABLE;
					br_flag	 = `ENABLE;
				end
				`ISA_OP_CALL  : begin // ¥³©`¥ë
					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
				/* ¥á¥â¥ê¥¢¥¯¥»¥¹ÃüÁî */
				`ISA_OP_LDW	  : begin // ¥ï©`¥ÉÕi¤ß³ö¤·
					alu_op	 = `ALU_OP_ADDU;
					alu_in_1 = imm_s;
					mem_op	 = `MEM_OP_LDW;
					gpr_we_	 = `ENABLE_;
				end
				`ISA_OP_STW	  : begin // ¥ï©`¥Éø¤­Þz¤ß
					alu_op	 = `ALU_OP_ADDU;
					alu_in_1 = imm_s;
					mem_op	 = `MEM_OP_STW;
				end
				/* ¥·¥¹¥Æ¥à¥³©`¥ëÃüÁî */
				`ISA_OP_TRAP  : begin // ¥È¥é¥Ã¥×
					exp_code = `ISA_EXP_TRAP;
				end
				/* ÌØØÃüÁî */
				`ISA_OP_RDCR  : begin // ÖÆÓù¥ì¥¸¥¹¥¿¤ÎÕi¤ß³ö¤·
					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 // ÖÆÓù¥ì¥¸¥¹¥¿¤Ø¤Îø¤­Þz¤ß
					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 // ÀýÍâ¤«¤é¤ÎÍ¢
					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 // Î´¶¨ÁxÃüÁî
					exp_code = `ISA_EXP_UNDEF_INSN;
				end
			endcase
		end
	end

endmodule