RISC-V项目(1)---搭建一个初步完整的CPU

这个文章我们从0开始实现一个CPU,完整地实现取指、译码、执行、回写四大操作过程。

1、PC计数器

  • 核心逻辑 (Logic Flow):
    ①复位 (Reset):rst_n 拉低时,PC 归零。
    ②跳转 (Jump):当 jump_en 为 1,PC 强制更新为目标地址 jump_addr(用于分支或函数调用)。
    ③顺序执行 (Fetch):默认情况下,PC 每周期 +4(对应 32 位指令字长),自动指向下一条指令。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
module pc_counter#(
parameter AW =32
)(
input logic clk,
input logic rst_n,
input logic jump_en,
input logic [AW-1:0] jump_addr,
output logic [AW-1:0] pc_pointer
);

always_ff@(posedge clk or negedge rst_n)begin
if(!rst_n)
pc_pointer<='h0;
else if(jump_en)
pc_pointer<=jump_addr;
else
pc_pointer<=pc_pointer+'h4;
end

endmodule

2、ROM存储器

  • 核心逻辑 (Logic Flow):
    ①初始化 (Initialization):利用 $readmemh 在仿真开始瞬间,从指定路径(如 rv32ui-p-addi.txt)读取十六进制指令,填满 4KB (4096 x 32bit) 的数组。
    ②字对齐 (Word Alignment):核心代码是 rom_reg[addr_in[AW-1:2]]。因为 PC 是按字节 (Byte) 寻址,而 ROM 是按 32位字 (Word) 存储,所以取地址时必须丢弃低两位(相当于除以 4),否则取到的指令全是错的。
    ③组合逻辑读取 (Asynchronous Read):使用 always_comb,数据输出不依赖时钟边沿,地址一变,指令立马出来。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
module rom #(
parameter AW=32,
parameter DW=32,
parameter file = "rv32ui-p-addi.txt"
)(
input logic [AW-1:0] addr_in,
output logic [DW-1:0] data_out
);

static string PATH="../test_data/";
static string full_path={PATH,file};

logic [DW-1:0] rom_reg [0:4095];

initial begin
$readmemh(full_path,rom_reg);
end

always_comb begin
data_out=rom_reg[addr_in[AW-1:2]];
end

endmodule

3、if2id

我们的CPU的使用流水线寄存器 (Pipeline Register),if2id体位于 取指 (IF) 和 译码 (ID) 阶段之间。它的作用是切断关键路径,让 CPU 能够跑得更快。

这个模块核心的功能就是:把pc寄存器给的地址,还有把从ROM取得的指令,用寄存器切割,然后再传到下一级

但是这里是涉及到流水线冲刷的,我们来用几个图简单看一下流水线冲刷机制是啥:

检测到有跳转指令后,进行指令冲刷

赋值为 NOP (No Operation),唯一的目的就是为了无害化处理。

为什么要保护那 32 个寄存器?(The Why)

  • CPU 的流水线最终都会汇聚到一个阶段:写回 (Write Back, WB)。
    正常情况:指令经过运算后,会把结果写进寄存器堆(Register File,那 32 个通用寄存器)。
    危险情况:如果刚才因为分支预测失败(Flush),流水线里残留了一条错误的指令(比如 add x1, x2, x3),如果不把它变成 NOP,它就会一路跑到 WB 阶段,把 x1 的值改掉。
    后果:程序逻辑直接崩盘,因为 x1 被错误地污染了。
    NOP 的作用就是告诉 WB 阶段:“别动!什么都别写!当作无事发生。

  • 在 RISC-V 中,NOP 其实是一个伪指令。它实际上是:$$\text{addi } x0, x0, 0$$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
`include "define.sv"

module if2id #(
parameter AW=32,
parameter DW=32
)(
input logic clk,
input logic rst_n,
input logic [AW-1:0] in_addr,
input logic [DW-1:0] in_instruction,
input logic instr_flush,
output logic [AW-1:0] out_addr,
output logic [DW-1:0] out_instruction
);

always_ff @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
out_addr<='h0;
out_instruction<=`INST_NOP;
end
else if(instr_flush)begin
out_addr<='h0;
out_instruction<=`INST_NOP;
end
else begin
out_addr<=in_addr;
out_instruction<=in_instruction;
end

end

endmodule

4、deine.sv指令解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
`timescale 1ns / 1ps

/****************************************Copyright (c)**************************************************
**----------------------------------------File Info-----------------------------------------------------
** File name : define
** Last modified Date : 2025-11-01
** Last Version : 1.0
** Descriptions : define
**------------------------------------------------------------------------------------------------------
*******************************************************************************************************/

// ===== 全局参数定义 =====
`define AW 32 // Address Width: 地址总线位宽 (32位 CPU)
`define DW 32 // Data Width: 数据总线位宽 (32位 CPU)
`define FILE "rv32ui-p-addi.dat" // 默认加载的测试文件 (仿真用)

// =========================================================
// I-Type 指令 (立即数运算)
// 格式: Opcode(7) + Rd(5) + Funct3(3) + Rs1(5) + Imm(12)
// =========================================================
`define INST_TYPE_I 7'b0010011 // I-Type 的主操作码 (Opcode)

// 下面是 Funct3 字段,用于区分具体的运算类型
`define INST_ADDI 3'b000 // 加立即数 (Add Immediate)
`define INST_SLTI 3'b010 // 小于置位立即数 (Set Less Than Immediate)
`define INST_SLTIU 3'b011 // 无符号小于置位立即数
`define INST_XORI 3'b100 // 异或立即数
`define INST_ORI 3'b110 // 或立即数
`define INST_ANDI 3'b111 // 与立即数
`define INST_SLLI 3'b001 // 逻辑左移立即数 (Shift Left Logical Imm)
`define INST_SRI 3'b101 // 右移立即数 (包含逻辑右移 SRLI 和 算术右移 SRAI,需靠 funct7 区分)

// =========================================================
// L-Type 指令 (加载指令 Load)
// 作用:从内存读数据到寄存器
// =========================================================
`define INST_TYPE_L 7'b0000011 // Load 指令的主操作码

`define INST_LB 3'b000 // Load Byte (加载字节,带符号扩展)
`define INST_LH 3'b001 // Load Halfword (加载半字/16位)
`define INST_LW 3'b010 // Load Word (加载字/32位)
`define INST_LBU 3'b100 // Load Byte Unsigned (加载字节,零扩展)
`define INST_LHU 3'b101 // Load Halfword Unsigned

// =========================================================
// S-Type 指令 (存储指令 Store)
// 作用:把寄存器的数据写回内存
// =========================================================
`define INST_TYPE_S 7'b0100011 // Store 指令的主操作码

`define INST_SB 3'b000 // Store Byte
`define INST_SH 3'b001 // Store Halfword
`define INST_SW 3'b010 // Store Word

// =========================================================
// R-Type & M-Type 指令 (寄存器-寄存器运算)
// 警告:标准运算(R)和乘除法扩展(M)共享同一个 Opcode!
// =========================================================
`define INST_TYPE_R_M 7'b0110011 // R-Type 的主操作码

// --- 标准 R-Type (基本 ALU 运算) ---
`define INST_ADD_SUB 3'b000 // 加法/减法 (靠 funct7 的第30位区分)
`define INST_SLL 3'b001 // 逻辑左移
`define INST_SLT 3'b010 // 小于置位
`define INST_SLTU 3'b011 // 无符号小于置位
`define INST_XOR 3'b100 // 异或
`define INST_SR 3'b101 // 逻辑右移/算术右移 (靠 funct7 区分)
`define INST_OR 3'b110 // 或
`define INST_AND 3'b111 // 与

// --- M-Extension (乘除法扩展) ---
// 注意:它们的 funct3 和上面的 R-Type 是重叠的!
// 例如 INST_MUL (000) 和 INST_ADD_SUB (000) 一样。
// 必须检查 funct7 字段是否为 0000001 才能确认为是乘除法。
`define INST_MUL 3'b000 // 乘法 (低32位)
`define INST_MULH 3'b001 // 乘法 (高32位,有符号)
`define INST_MULHSU 3'b010 // 乘法 (高32位,有符号x无符号)
`define INST_MULHU 3'b011 // 乘法 (高32位,无符号)
`define INST_DIV 3'b100 // 除法 (有符号)
`define INST_DIVU 3'b101 // 除法 (无符号)
`define INST_REM 3'b110 // 取余 (有符号)
`define INST_REMU 3'b111 // 取余 (无符号)

// =========================================================
// J-Type & U-Type (跳转与长立即数)
// =========================================================
`define INST_JAL 7'b1101111 // JAL 跳转并链接 (直接跳转)
`define INST_JALR 7'b1100111 // JALR 寄存器跳转 (间接跳转)

`define INST_LUI 7'b0110111 // 加载高位立即数 (Load Upper Imm)
`define INST_LUIPC 7'b0010111 // 加载高位立即数加到 PC (PC相对寻址核心)

// =========================================================
// 特殊指令与伪指令
// =========================================================
`define INST_NOP 32'h00000013 // 空指令 (实际上是 addi x0, x0, 0)
`define INST_NOP_OP 7'b0000001 // 这个定义有点奇怪,可能是自定义的 NOP 操作码?
`define INST_MRET 32'h30200073 // 从机器模式异常返回 (Machine Return)
`define INST_RET 32'h00008067 // 函数返回 (实际上是 jalr x0, x1, 0)

`define INST_FENCE 7'b0001111 // 内存屏障 (用于多核或乱序执行)
`define INST_ECALL 32'h00000073 // 环境调用 (系统调用 System Call)
`define INST_EBREAK 32'h00100073 // 环境断点 (调试用 Debug)

// =========================================================
// B-Type 指令 (分支 Branch)
// =========================================================
// 原代码注释写错了 "J type inst",这是 B-Type!
`define INST_TYPE_B 7'b1100011 // Branch 指令主操作码

`define INST_BEQ 3'b000 // 相等跳转 (Branch if Equal)
`define INST_BNE 3'b001 // 不等跳转 (Branch if Not Equal)
`define INST_BLT 3'b100 // 小于跳转 (Branch if Less Than)
`define INST_BGE 3'b101 // 大于等于跳转
`define INST_BLTU 3'b110 // 无符号小于跳转
`define INST_BGEU 3'b111 // 无符号大于等于跳转

5、通用寄存器register

  • 2R1W 结构:支持 2路读 (read_rs1, read_rs2) 和 1路写 (ex_write)。这是现代 RISC-V 整数流水线的标准配置。

  • 32个寄存器:通过 regs[0:31] 定义了 x0 到 x31。

  • x0 恒零保护 (Zero Register Protection)
    读的时候:read_rs1_addr == ‘h0 时,强制输出 0。
    写的时候:ex_write_addr != ‘h0 时才允许写入。
    作用:完美符合 RISC-V 规范,确保 x0 永远是 0,怎么写都没用。

  • 内部前递 (Internal Forwarding / Write-through) —— 这段代码的精华!

1
2
else if(ex_wirite_en && (read_rs1_addr == ex_write_addr))
out_rs1_data = ex_write_data; // 直接输出写入的数据

场景:如果在同一个时钟周期内,你既要读地址 x5,又要往 x5 写新值。如果没有这段逻辑,读出来的就是“旧值”;有了这段逻辑,读出来的就是“新值”。这能让流水线少停顿一个周期。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
module register #(
parameter DW=32
)(
input logic clk,
input logic rst_n,
input logic [4:0] read_rs1_addr,
input logic [4:0] read_rs2_addr,

output logic [DW-1:0] out_rs1_data,
output logic [DW-1:0] out_rs2_data,

input logic ex_write_en,
input logic [4:0] ex_write_addr,
input logic [DW-1:0] ex_write_data
);

logic [DW-1:0] regs[0:31];

always_comb begin
if(!rst_n)
out_rs1_data='h0;
else if(read_rs1_addr=='h0)
out_rs1_data='h0;
else if(ex_wirite_en&&(read_rs1_addr==ex_write_addr))
out_rs1_data=ex_write_data;
else
out_rs1_data=regs[read_rs1_addr];
end

always_comb begin
if(!rst_n)
out_rs2_data='h0;
else if(read_rs2_addr=='h0)
out_rs2_data='h0;
else if(ex_write_en&&(read_rs2_addr==ex_write_addr))
out_rs2_data=ex_write_data;
else
out_rs2_data=regs[read_rs2_addr];
end

always_ff@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
for(int i=0;i<32;i++)begin
regs[i]<='h0;
end
end
else if(ex_write_en&&(ex_write_addr!='h0))
regs[ex_write_addr]<=ex_write_data;
end

endmodule

6、decoder

①JALR指令:
jalr 指令使用 12 位立即数(有符号数)作为偏移量,与操作数寄存器 rs1中的值相加,然后将结果的最低有效位置0。jalr指令将其下一条指令的 PC(即当前指令PC+4)的值写入其结果寄存器 rd。

立即数为20位到31位的指令:

L型指令:

S型指令:

B型指令:

模块名称:Decoder (译码器) 核心功能:

  • 切片 (Slicing):将 32 位指令切割成 opcode, rs1, rs2, rd, funct3/7。
  • 立即数生成 (Imm Gen):根据指令类型(I/S/B/J/U),将碎片化的立即数拼接并扩展为 32 位。
  • 操作数分发 (Operand Mux):根据指令类型,决定 ALU 的两个输入操作数 (op1, op2) 到底来自寄存器堆 (rs_data)、PC (in_addr) 还是立即数 (imm)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
`timescale 1ns /1ps                                                                                     

`include "define.sv"

module decode #(
parameter AW = 32 ,
parameter DW = 32
)(
input logic [AW-1:0] instr_addr_in//PC寄存器的地址 ,
input logic [DW-1:0] instr_in //ROM中存放的指令instruction ,
//to register
output logic [4:0] rd_rs1_addr //解析指令后获取源地址1 ,
output logic [4:0] rd_rs2_addr //解析指令后获取源地址2 ,
//from register
input logic [DW-1:0] rd_rs1_data //把源地址1给通用寄存器后获取到的数据1 ,
input logic [DW-1:0] rd_rs2_data //把源地址2给通用寄存器后获取到的数据2 ,
//to instr execute
output logic [DW-1:0] op1_out //把操作数1传出去 ,
output logic [DW-1:0] op2_out //把操作数2传出去
);

logic [6:0] opcode ;
logic [4:0] rd ;
logic [2:0] func3 ;
logic [4:0] rs1 ;
logic [4:0] rs2 ;
logic [6:0] func7 ;
logic [31:0] imm ;


assign opcode = instr_in[6:0] ;
assign rd = instr_in[11:7] ;
assign func3 = instr_in[14:12] ;
assign rs1 = instr_in[19:15] ;
assign rs2 = instr_in[24:20] ;
assign func7 = instr_in[31:25] ;

always_comb begin
case(opcode)
`INST_TYPE_I,`INST_TYPE_L,`INST_JALR:
imm = {{20{instr_in[31]}},instr_in[31:20]};
`INST_TYPE_S:
imm = {{20{instr_in[31]}},instr_in[31:25],instr_in[11:7]};
`INST_TYPE_B:
imm = {{20{instr_in[31]}},instr_in[7],instr_in[30:25],instr_in[11:8],1'b0};
`INST_JAL:
imm = {{12{instr_in[31]}},instr_in[19:12],instr_in[20],instr_in[30:21], 1'b0};
`INST_LUI,`INST_LUIPC:
imm = {instr_in[31:12],12'h0};
default:imm = 32'h0;
endcase
end

always_comb begin
case(opcode)
`INST_TYPE_I:begin
case(func3)
`INST_ADDI:begin
rd_rs1_addr = rs1 ;
rd_rs2_addr = 5'h0 ;
op1_out = rd_rs1_data ;
op2_out = imm ;
end
default:begin
rd_rs1_addr = 'h0 ;
rd_rs2_addr = 'h0 ;
op1_out = 'h0 ;
op2_out = 'h0 ;
end
endcase
end
`INST_TYPE_R_M:begin
case(func3)
`INST_ADD_SUB:begin
rd_rs1_addr = rs1 ;
rd_rs2_addr = rs2 ;
op1_out = rd_rs1_data ;
op2_out = rd_rs2_data ;
end
default:begin
rd_rs1_addr = 'h0 ;
rd_rs2_addr = 'h0 ;
op1_out = 'h0 ;
op2_out = 'h0 ;
end
endcase
end
`INST_TYPE_B:begin
case(func3)
`INST_BNE:begin
rd_rs1_addr = rs1 ;
rd_rs2_addr = rs2 ;
op1_out = rd_rs1_data ;
op2_out = rd_rs2_data ;
end
default:begin
rd_rs1_addr = 'h0 ;
rd_rs2_addr = 'h0 ;
op1_out = 'h0 ;
op2_out = 'h0 ;
end
endcase
end
`INST_JAL:begin
rd_rs1_addr = 5'h0 ;
rd_rs2_addr = 5'h0 ;
op1_out = 32'h0 ;
op2_out = imm ;
end
`INST_JALR:begin
rd_rs1_addr = rs1 ;
rd_rs2_addr = 5'h0 ;
op1_out = rd_rs1_data ;
op2_out = imm ;
end
`INST_LUI:begin
rd_rs1_addr = 5'h0 ;
rd_rs2_addr = 5'h0 ;
op1_out = 32'h0 ;
op2_out = imm ;
end
`INST_LUIPC:begin
rd_rs1_addr = 5'h0 ;
rd_rs2_addr = 5'h0 ;
op1_out = instr_addr_in ;
op2_out = imm ;
end
`INST_NOP_OP: begin
rd_rs1_addr = 5'h0 ;
rd_rs2_addr = 5'h0 ;
op1_out = 32'h0 ;
op2_out = 32'h0 ;
end
default:begin
rd_rs1_addr = 'h0 ;
rd_rs2_addr = 'h0 ;
op1_out = 'h0 ;
op2_out = 'h0 ;
end
endcase
end

endmodule

7、id2ex

instruction to excute

核心位置:位于 译码阶段 (ID) 和 执行阶段 (EX) 之间。

  • 主要功能:打拍(隔离)。在时钟上升沿,将译码出的 PC地址、指令码、操作数 1 (op1)、操作数 2 (op2) 锁存并传递给执行单元。

  • instr_flush 信号在这里 不是“保持” (Stall),而是 “冲刷” (Flush)。

  • 当 instr_flush 为 1 时,它把输出强制设为 NOP (空指令) 和 0,相当于向流水线插入了一个气泡。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
`include "define.sv"

module id2ex #(
parameter AW = 32 ,
parameter DW = 32
)(
input logic clk ,
input logic rst_n ,
input logic instr_flush ,
input logic [AW-1:0] instr_addr_in ,
input logic [DW-1:0] instr_in ,
input logic [DW-1:0] op1_in ,
input logic [DW-1:0] op2_in ,
//to execute
output logic [AW-1:0] instr_addr_out ,
output logic [DW-1:0] instr_out ,
output logic [DW-1:0] op1_out ,
output logic [DW-1:0] op2_out
);


always_ff @(posedge clk or negedge rst_n)
if(!rst_n) begin
instr_addr_out <= 'h0;
instr_out <= `INST_NOP;
op1_out <= 'h0;
op2_out <= 'h0;
end
else if(instr_flush) begin
instr_addr_out <= 'h0;
instr_out <= `INST_NOP;
op1_out <= 'h0;
op2_out <= 'h0;
end
else begin
instr_addr_out <= instr_addr_in;
instr_out <= instr_in ;
op1_out <= op1_in ;
op2_out <= op2_in ;
end

8、excute

我们重点来看一下B型指令的组成:

    1. 第 0 位 (imm[0]):强制填 0
      来源:无(不需要指令提供)。
      操作:直接补一个 0。
      为什么:RISC-V 规定跳转地址必须是 2 的倍数(偶数),所以最低位永远是 0。
    1. 第 1~4 位 (imm[4:1]):抓指令的 [11:8]
      来源:instr[11:8]
      操作:直接搬过来。
    1. 第 5~10 位 (imm[10:5]):抓指令的
      来源:instr[30:25]
      操作:直接搬过来。
    1. 第 11 位 (imm[11]):抓指令的 [7]
      来源:instr[7]
      注意:这就是最“乱”的那一位,它独自藏在指令的低位里。
    1. 第 12 位 (imm[12]):抓指令的 [31]
      来源:instr[31] (最高位)
      意义:这是符号位(正负号)。
    1. 第 13~31 位 (imm[31:13]):复制符号位
      来源:还是 instr[31]
      操作:把第 12 位拿到的那个数(0或1),疯狂复制 19 次,填满剩下的高位。这叫符号扩展。

我们来看一下B型、JAL型、JALR型指令地址的转换是怎么计算的

  • B型 / JAL:以 PC 为基准起跳 (PC + imm)。
  • JALR:以 寄存器 (rs1) 为基准起跳 (rs1 + imm)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
`timescale 1ns / 1ps

// ==========================================
// 模块功能:执行阶段 (EX Stage)
// 核心任务:
// 1. 算术运算 (加减法)
// 2. 跳转目标地址计算 (Branch/JAL/JALR)
// 3. 生成写回寄存器的数据 (Result)
// ==========================================

`include "define.sv" // 包含指令操作码宏定义

module execute #(
parameter AW = 32, // 地址位宽
parameter DW = 32 // 数据位宽
)(
// --- Input Ports (来自 ID/EX 寄存器) ---
input  logic [AW-1:0] instr_addr, // 当前指令 PC
input logic [DW-1:0] instr, // 当前指令二进制码
input   logic [DW-1:0] op1, // 操作数 1 (rs1)
input   logic [DW-1:0] op2, // 操作数 2 (rs2 或 imm)

// --- Output Ports (去往 MEM/WB 阶段或 寄存器堆) ---
output logic          wr_reg_en, // 寄存器写使能
output logic [4:0] wr_reg_addr, // 目标寄存器 rd
output logic [DW-1:0] wr_reg_data, // 写回的数据 (Result)

// --- Output Ports (去往 Control Unit 控制跳转) ---
output logic          jump_en, // 跳转请求 (1=跳)
output logic [AW-1:0]  jump_addr, // 跳转目标地址
output logic          jump_hold // [注意] 流水线控制信号 (Hold? Flush?)
);


// --- Internal Signals ---
logic [6:0] opcode; // 主操作码
logic [4:0] rd; // 目标寄存器索引
logic [2:0]  func3; // 功能码 3位
logic [6:0]  func7;  // 功能码 7位
logic [31:0]  imm; // B-Type 立即数
logic equal; // 比较结果
logic [AW-1:0] jump_imm; // B-Type 跳转目标


// --- Instruction Decoding (硬连线切片) ---
assign opcode = instr[6:0];
assign  rd = instr[11:7];
assign  func3 = instr[14:12];
assign  func7  = instr[31:25];

// --- B-Type Immediate Generation ---
// 复杂的位拼接,用于还原乱序存储的 B 型指令立即数,并做符号扩展
assign imm = {{20{instr[31]}},instr[7],instr[30:25],instr[11:8],1'b0};

// --- Comparator (用于 BNE/BEQ) ---
assign equal = (op1 == op2) ? 1'b1 : 1'b0;

// --- PC-Relative Address Calculation (用于 B-Type) ---
// 当前 PC + 偏移量 = 目标 PC
assign jump_imm = instr_addr + imm;


// --- Main Execution Logic (Combinational) ---
always_comb begin
case(opcode)
// ------------------------------------
// I-Type Instructions (Immediate Ops)
// ------------------------------------
`INST_TYPE_I:begin
case(func3)
`INST_ADDI:begin // ADDI: rd = rs1 + imm
wr_reg_en  = 1'b1;
wr_reg_addr  = rd;
wr_reg_data = op1 + op2; // op2 应该是 Decode 阶段选好的 imm
jump_en = 1'b0;
jump_addr = 'h0;
jump_hold  = 1'b0;
end
default:begin // Default behavior for undefined I-type
wr_reg_en  = 1'b0;
wr_reg_addr  = 5'h0;
wr_reg_data = 'h0;
jump_en = 1'b0;
jump_addr = 'h0;
jump_hold  = 1'b0;
end
endcase
end

// ------------------------------------
// R-Type / M-Type (Register Ops)
// ------------------------------------
`INST_TYPE_R_M:begin
case(func3)
`INST_ADD_SUB:begin
// 根据 func7 区分 ADD (0000000) 和 SUB (0100000)
if(func7 == 7'b000_0000) begin // ADD: rd = rs1 + rs2
wr_reg_en  = 1'b1;
wr_reg_addr  = rd;
wr_reg_data = op1 + op2;
jump_en = 1'b0;
jump_addr = 'h0;
jump_hold  = 1'b0;
end
else begin // SUB: rd = rs1 - rs2
wr_reg_en  = 1'b1;
wr_reg_addr  = rd;
wr_reg_data = op1 - op2;
jump_en = 1'b0;
jump_addr = 'h0;
jump_hold  = 1'b0;
end
end
default:begin
wr_reg_en  = 1'b0;
wr_reg_addr  = 5'h0;
wr_reg_data = 'h0;
jump_en = 1'b0;
jump_addr = 'h0;
jump_hold  = 1'b0;
end
endcase
end

// ------------------------------------
// B-Type (Branch Instructions)
// ------------------------------------
`INST_TYPE_B: begin
case(func3)
`INST_BNE: begin // BNE: Branch if Not Equal
wr_reg_en  = 1'b0; // 分支指令不写通用寄存器
wr_reg_addr  = 5'h0;
wr_reg_data = 'h0;
// 如果不相等 (~equal),则跳转使能置 1
jump_en = ~equal;
// 跳转目标设为算好的 jump_imm
jump_addr = ~equal ? jump_imm : 'h0;
// [红色警报] 这里依然是 0。
// 如果架构设计需要 EX 级发出冲刷信号,这里必须是 ~equal (即跟 jump_en 一致)
jump_hold  = 1'b0;
end
default:begin
wr_reg_en  = 1'b0;
wr_reg_addr  = 5'h0;
wr_reg_data = 'h0;
jump_en = 1'b0;
jump_addr = 'h0;
jump_hold  = 1'b0;
end
endcase
end

// ------------------------------------
// JAL (Jump and Link)
// ------------------------------------
`INST_JAL:begin
wr_reg_en  = 1'b1;
wr_reg_addr   = rd;
wr_reg_data = instr_addr + 32'h4; // 链接:保存返回地址 (PC+4)
jump_en = 1'b1; // 强制跳转
// JAL 目标地址 = PC + imm (op2 必须是 decode 传来的 imm)
jump_addr = instr_addr + op2;
// [红色警报] 这里也应该是 1 (如果是冲刷逻辑)
jump_hold  = 1'b0;
end

// ------------------------------------
// JALR (Jump and Link Register)
// ------------------------------------
`INST_JALR:begin
wr_reg_en  = 1'b1;
wr_reg_addr   = rd;
wr_reg_data = instr_addr + 32'h4; // 链接
jump_en = 1'b1; // 强制跳转
// JALR 目标地址 = rs1 + imm (即 op1 + op2)
jump_addr = op1 + op2;
// [红色警报] 这里也应该是 1
jump_hold  = 1'b0;
end

// ------------------------------------
// LUI (Load Upper Immediate)
// ------------------------------------
`INST_LUI:begin
wr_reg_en  = 1'b1;
wr_reg_addr   = rd;
wr_reg_data = op2; // op2 在 decode 阶段已处理为 {imm, 12'h0}
jump_en = 1'b0;
jump_addr = 'h0;
jump_hold  = 1'b0;
end

// ------------------------------------
// LUIPC (Load Upper Immediate to PC)
// ------------------------------------
`INST_LUIPC:begin
wr_reg_en  = 1'b1;
wr_reg_addr   = rd;
wr_reg_data = op1 + op2; // PC + imm
jump_en = 1'b0;
jump_addr = 'h0;
jump_hold  = 1'b0;
end

// ------------------------------------
// NOP (No Operation)
// ------------------------------------
`INST_NOP_OP:begin
wr_reg_en  = 1'b0;
wr_reg_addr  = 5'h0;
wr_reg_data = 'h0;
jump_en = 1'b0;
jump_addr = 'h0;
jump_hold  = 1'b0;
end

// ------------------------------------
// Default (Undefined Instruction)
// ------------------------------------
default:begin
wr_reg_en  = 1'b0;
wr_reg_addr  = 5'h0;
wr_reg_data = 'h0;
// [注意] 默认行为是跳转到 0 地址重启
jump_en = 1'b1;
jump_addr = 'h0;
jump_hold  = 1'b0;
end
endcase
end

endmodule
ESC 关闭 | 导航 | Enter 打开
输入关键词开始搜索