上一篇博文中我们对整个 CPU 的逻辑部件进行了概述(这里是传送门),在这篇博文里会继续分析对于 CPU 实现指令的选取,以及选取后各个部件的控制信号选取,并提供核心示例代码和整个系统的源码。
博客内所有文章均为 原创,所有示意图均为 原创,若转载请附原文链接。
该 CPU 设计以实际的 RISC-V 指令系统 RV32I 为基准,选取指令集六种指令格式中具有代表性的九条指令来进行实现,六种指令格式如下所示。
其次选取的九条指令如下表所示:
格式 | 指令 | 操作 |
---|---|---|
R- 型 | add | rd,rs1,rs2 |
R- 型 | slt | rd,rs1,rs2 |
R- 型 | sltu | rd,rs1,rs2 |
I- 型 | ori | rd,rs1,imm12 |
I- 型 | lw | rd,rs1,imm12 |
U- 型 | lui | rd,imm20 |
S- 型 | sw | rs1,rs2,imm12 |
B- 型 | beq | rs1,rs2,imm12 |
J- 型 | jal | rd,imm20 |
指令 | 功能 | 说明 |
---|---|---|
add rd, rsl, rs2 | PC←PC+4 R[rd]←R[rs1] + R[rs2] |
从 PC 所指的内存单元中取指令,并 PC 加 4 从rsl、rs2 中取数后相加,结果送rd (不进行溢出判断) |
slt rd, rs1, rs2 | if( R[rs1]< R[rs2]) R[rd]←1 else R[rd]← 0 |
从 rs1、rs2 中取数后按带符号整数来判断两数大小,小于则 rd 中置 1 否则,rd 中清 0 (不进行溢出判断) |
sltu rd, rs1, rs2 | if(R[rs1] < R[rs2]) R[rd]←1 else R[rd]← 0 |
从 rs1、rs2 中取数后按无符号数来判断两数大小,小于则 rd 中置 1 否则,rd 中清 0 (不进行溢出判断) |
ori rd, rs1, imm12 | R[rd]←R[rs1] l SEXT ( imm12) | 从 rs1 取数、将 imm12 进行符号扩展,然后两者按位或,结果送 rd |
lui rd, imm20 | R[rd]←imm20 II 000H | rd高20位为imm20,低12位为0,符号 ll 表示 “拼接” |
lw rd, rs1, imm12 | Addr←R[rs1] + SEXT ( imm12 ) R[rd]←M[Addr] |
从 rs1 取数、将 imm12 进行符号扩展,然后两者相加, 结果作为访存地址 Addr,从 Addr 中取数并送 rd |
sw rs1, rs2, imm12 | Addr←R[rs1] + SEXT ( imm12) M[Addr]←R[rs2] |
从 rs1 取数、将 imm12 进行符号扩展,然后两者相加, 结果作为访存地址 Addr ,将 rs2 送 Addr 中 |
beq rsl, rs2, imm12 | Cond←R[rs1] - R[rs2] if (Cond eq 0) PC←PC+(SEXT (imm12) x 2) |
做减法以比较rsl和rs2中内容的大小,并计算下条指令地址,然后根据比较结果修改PC。 转移目标地址采用相对寻址,基准地址为当前指令地址(即PC), 偏移量为立即数imm12经符号扩展后的值的2倍。 因此在RV321中,beq 指令转移目标的指令范围为当前指令的前1024到后1023条指令。 |
jal rd, imm20 | R[rd]←PC+4 PC←PC +( SEXT( imm20) x 2) |
PC+4的结果送rd但不送PC,然后计算下条指令地址。 转移地址采用相对寻址,基准地址为当前指令地址(即PC), 偏移量为立即数imm20经符扩展后的值的2倍。 因此在RV32I中,jal 指令转移目标的指令范围为当前指令的前262144到后262143条指令。 |
指令 | 功能 | 立即数编码类型 | ExtOp<2:0> |
---|---|---|---|
add rd, rsl, rs2 | PC←PC+4 R[rd]←R[rs1] + R[rs2] |
无立即数 | X X X |
slt rd, rs1, rs2 | if( R[rs1]< R[rs2]) R[rd]←1 else R[rd]← 0 |
无立即数 | X X X |
sltu rd, rs1, rs2 | if(R[rs1] < R[rs2]) R[rd]←1 else R[rd]← 0 |
无立即数 | X X X |
ori rd, rs1, imm12 | R[rd]←R[rs1] l SEXT ( imm12) | I- 型立即数( immI ) | 0 0 0 |
lui rd, imm20 | R[rd]←imm20 II 000H | U- 型立即数( immU ) | 0 0 1 |
lw rd, rs1, imm12 | Addr←R[rs1] + SEXT ( imm12 ) R[rd]←M[Addr] |
I- 型立即数( immI ) | 0 0 0 |
sw rs1, rs2, imm12 | Addr←R[rs1] + SEXT ( imm12) M[Addr]←R[rs2] |
S- 型立即数 | 0 1 0 |
beq rsl, rs2, imm12 | Cond←R[rs1] - R[rs2] if (Cond eq 0) PC←PC+(SEXT (imm12) x 2) |
B- 型立即数( immB ) | 0 1 1 |
jal rd, imm20 | R[rd]←PC+4 PC←PC +( SEXT( imm20) x 2) |
J- 型立即数 | 1 0 0 |
指令 | 功能 | 运算类型 | SUBctr | SIGctr | OPctr<1:0> |
---|---|---|---|---|---|
add rd, rsl, rs2 | PC←PC+4 R[rd]←R[rs1] + R[rs2] |
加 | 0 | X | 0 0 |
slt rd, rs1, rs2 | if( R[rs1]< R[rs2]) R[rd]←1 else R[rd]← 0 |
减 带符号整数比较大小 |
1 | 1 | 1 1 |
sltu rd, rs1, rs2 | if(R[rs1] < R[rs2]) R[rd]←1 else R[rd]← 0 |
减 无符号整数比较大小 |
1 | 0 | 1 1 |
ori rd, rs1, imm12 | R[rd]←R[rs1] l SEXT ( imm12) | 按位或 | X | X | 0 1 |
lui rd, imm20 | R[rd]←imm20 II 000H | 操作数 B 选择 | X | X | 1 0 |
lw rd, rs1, imm12 | Addr←R[rs1] + SEXT ( imm12 ) R[rd]←M[Addr] |
加 | 0 | X | 0 0 |
sw rs1, rs2, imm12 | Addr←R[rs1] + SEXT ( imm12) M[Addr]←R[rs2] |
加 | 0 | X | 0 0 |
beq rsl, rs2, imm12 | Cond←R[rs1] - R[rs2] | 减(判 0) | 1 | X | X X |
beq rsl, rs2, imm12 | if (Cond eq 0) PC←PC+(SEXT (imm12) x 2) |
加 | 0 | X | 0 0 |
jal rd, imm20 | R[rd]←PC+4 PC←PC +( SEXT( imm20) x 2) |
加 | 0 | X | 0 0 |
ALUctr<3:0> | 操作类型 | SUBctr | SIGctr | OPctr<1:0> | OPctr 的含义 |
---|---|---|---|---|---|
0 0 0 0 | add | 0 | X | 0 0 | 选择加法器的结果输出 |
0 0 0 1 | (未用) | ||||
0 0 1 0 | slt | 1 | 1 | 1 1 | 选择小于置位结果输出 |
0 0 1 1 | sltu | 1 | 0 | 1 1 | 选择小于置位结果输出 |
0 1 0 0 | (未用) | ||||
0 1 0 1 | (未用) | ||||
0 1 1 0 | or | X | X | 0 1 | 选择“按位或”结果输出 |
0 1 1 1 | (未用) | ||||
1 0 0 0 | sub | 1 | X | 0 0 | 选择加法器的结果输出 |
其余 | (未用) | ||||
1 1 1 1 | srcB | X | X | 1 0 | 选择操作数 B 直接输出 |
funct3 op 控制信号 |
000 0110011 add |
010 0110011 slt |
011 0110011 sltu |
110 0010011 ori |
无关 0110111 lui |
010 0000011 lw |
010 0100011 sw |
000 1100011 beq |
无关 1101111 jal |
---|---|---|---|---|---|---|---|---|---|
Branch | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
Jump | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
ALUAsrc | 0 | 0 | 0 | 0 | X | 0 | 0 | 0 | 1 |
ALUBsrc<1:0> | 00 | 00 | 00 | 10 | 10 | 10 | 10 | 00 | 01 |
ALUctr<3:0> | 0000 (add) |
0010 (slt) |
0011 (sltu) |
0110 (or) |
1111 (srcB) |
0000 (add) |
0000 (add) |
1000 (sub) |
0000 (add) |
MemtoReg | 0 | 0 | 0 | 0 | 0 | 1 | X | X | 0 |
RegWr | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 1 |
MemWr | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
ExtOp<2:0> | X | X | X | 000 immI |
001 immU |
000 immI |
010 immS |
011 immB |
100 immJ |
这里只列出立即数扩展的 Verilog 表达式,对于每个具体指令的立即数扩展格式可参照上方其对应的指令格式。
// ie.v
case(ext_op)
3'b000: begin // ori lw immI
imm <= {{20{instr[31]}} , instr[31:20]};
end
3'b001: begin // lui immU
imm <= {instr[31:12], 12'b0};
end
3'b010: begin // sw immS
imm <= {{20{instr[31]}}, instr[31:25], instr[11:7]};
end
3'b011: begin // beq immB
imm <= {{20{instr[31]}}, instr[7], instr[30:25], instr[11:8], 1'b0};
end
3'b100: begin // jal immJ
imm <= {{12{instr[31]}}, instr[19:12], instr[20], instr[30:21], 1'b0};
end
default: begin
end
endcase
// alu_ctr.v
always @ (*) begin
sub_ctr <= (~alu_ctr[3] & ~alu_ctr[2] & alu_ctr[1]) | alu_ctr[3];
sig_ctr <= ~alu_ctr[0];
op_ctr[1] <= (~alu_ctr[3] & ~alu_ctr[2] & alu_ctr[1]) | (alu_ctr[3] & alu_ctr[2] & alu_ctr[1] & alu_ctr[0]);
op_ctr[0] <= (~alu_ctr[3] & ~alu_ctr[2] & alu_ctr[1]) | (~alu_ctr[3] & alu_ctr[2] & alu_ctr[1] & ~alu_ctr[0]);
end
// id.v
always @ (*) begin
if(~rst_n) begin // 清零重置
wd_o <= 0;
reg1_addr_o <= 0;
reg2_addr_o <= 0;
end
else begin // 译码
wd_o <= inst_i[11:7]; // 写寄存器地址
reg1_addr_o <= inst_i[19:15]; // 读寄存器 A 地址
reg2_addr_o <= inst_i[24:20]; // 读寄存器 B 地址
// 单值控制信号
branch_o <= op[6]&op[5]&~op[4]&~op[3]&~op[2]&op[1]&op[0]; // B-type
jump_o <= op[6]&op[5]&~op[4]&op[3]&op[2]&op[1]&op[0]; // J-type
mem_to_reg_o <= ~op[6]&~op[5]&~op[4]&~op[3]&~op[2]&op[1]&op[0]; // Load
reg_wr_o <= (~op[6]&op[5]&op[4]&~op[3]&~op[2]&op[1]&op[0]) // R-type
| (~op[6]&~op[5]&op[4]&~op[3]&~op[2]&op[1]&op[0]) // I-type-ALU
| (~op[6]&op[5]&op[4]&~op[3]&op[2]&op[1]&op[0]) // lui
| (~op[6]&~op[5]&~op[4]&~op[3]&~op[2]&op[1]&op[0]) // Load
| (op[6]&op[5]&~op[4]&op[3]&op[2]&op[1]&op[0]); // J-type
mem_wr_o <= ~op[6]&op[5]&~op[4]&~op[3]&~op[2]&op[1]&op[0]; // Store
alu_asrc_o <= op[6]&op[5]&~op[4]&op[3]&op[2]&op[1]&op[0]; // J-type
// 多值控制信号
alu_bsrc_o[1] <= (~op[6]&~op[5]&op[4]&~op[3]&~op[2]&op[1]&op[0]) // I-type-ALU
| (~op[6]&op[5]&op[4]&~op[3]&op[2]&op[1]&op[0]) // lui
| (~op[6]&~op[5]&~op[4]&~op[3]&~op[2]&op[1]&op[0]) // Load
| (~op[6]&op[5]&~op[4]&~op[3]&~op[2]&op[1]&op[0]); // Store
alu_bsrc_o[0] <= op[6]&op[5]&~op[4]&op[3]&op[2]&op[1]&op[0]; // J-type
ext_op_o[2] <= op[6]&op[5]&~op[4]&op[3]&op[2]&op[1]&op[0]; // J-type
ext_op_o[1] <= (op[6]&op[5]&~op[4]&~op[3]&~op[2]&op[1]&op[0]) // B-type
| (~op[6]&op[5]&~op[4]&~op[3]&~op[2]&op[1]&op[0]); // Store
ext_op_o[0] <= (~op[6]&op[5]&op[4]&~op[3]&op[2]&op[1]&op[0]) // lui
| (op[6]&op[5]&~op[4]&~op[3]&~op[2]&op[1]&op[0]); // B-type
alu_ctr_o[3] <= (~op[6]&op[5]&op[4]&~op[3]&op[2]&op[1]&op[0]) // lui
| (op[6]&op[5]&~op[4]&~op[3]&~op[2]&op[1]&op[0]); // B-type
alu_ctr_o[2] <= ((~op[6]&op[5]&op[4]&~op[3]&~op[2]&op[1]&op[0]) // R-type
| (~op[6]&~op[5]&op[4]&~op[3]&~op[2]&op[1]&op[0])) // I-type-ALU
& fn[2]
| (~op[6]&op[5]&op[4]&~op[3]&op[2]&op[1]&op[0]); // lui
alu_ctr_o[1] <= ((~op[6]&op[5]&op[4]&~op[3]&~op[2]&op[1]&op[0]) // R-type
| (~op[6]&~op[5]&op[4]&~op[3]&~op[2]&op[1]&op[0])) // I-type-ALU
& fn[1]
| (~op[6]&op[5]&op[4]&~op[3]&op[2]&op[1]&op[0]); // lui
alu_ctr_o[0] <= ((~op[6]&op[5]&op[4]&~op[3]&~op[2]&op[1]&op[0]) // R-type
| (~op[6]&~op[5]&op[4]&~op[3]&~op[2]&op[1]&op[0])) // I-type-ALU
& fn[0]
| (~op[6]&op[5]&op[4]&~op[3]&op[2]&op[1]&op[0]); // lui
end
end
https://github.com/TIYangFan/CPU-Design-Based-on-RISC-V(如果可以帮到你,请帮忙 Star ~)