文章目錄
一、概述
上一篇博文中我們對整個 CPU 的邏輯部件進行了概述(這裏是傳送門),在這篇博文裏會繼續分析對於 CPU 實現指令的選取,以及選取後各個部件的控制信號選取,並提供核心示例代碼和整個系統的源碼。
博客內所有文章均爲 原創,所有示意圖均爲 原創,若轉載請附原文鏈接。
二、設計過程
2.1 選取指令
該 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 |
2.2 指令功能簡述
指令 | 功能 | 說明 |
---|---|---|
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條指令。 |
2.3 設計過程概述
- 第一步:分析每條指令的功能;
- 第二步:根據指令的功能給出所需的元件,並考慮如何將它們互連;
- 第三步:確定每個元件所需控制信號的取值;
- 第四步:彙總所有指令涉及的控制信號,生成反映指令與控制信號之間的關係表;
- 第五步:根據關係表,得到每個控制信號的邏輯表達式,據此設計控制電路;
2.4 擴展碼取值
指令 | 功能 | 立即數編碼類型 | 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 |
示意圖如下:
2.5 三種 ALU 操作信號
2.5.1 操作信號取值
指令 | 功能 | 運算類型 | 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 |
2.5.1 操作信號編碼
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 直接輸出 |
2.6 控制信號取值
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 |
三、代碼實現
3.1 擴展碼取值
這裏只列出立即數擴展的 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
3.2 ALU 控制信號取值
// 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
3.3 CPU 控制信號取值
// 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
3.4 邏輯代碼結構分析
3.5 仿真代碼結構分析
3.6 其餘文件分析
3.6.1 inst_rom.data 指令存儲器
3.6.2 data_rom.data 數據存儲器
四、CPU 整體原理圖
五、源碼地址
https://github.com/TIYangFan/CPU-Design-Based-on-RISC-V(如果可以幫到你,請幫忙 Star ~)
六、參考資料
- 《計算機組成與設計(基於 RISC-V 架構)》—— 袁春風 餘子濠