基於 RICS-V 架構的單週期處理器設計(含所有格式指令)—— 控制信號選取及代碼結構分析

一、概述

  上一篇博文中我們對整個 CPU 的邏輯部件進行了概述(這裏是傳送門),在這篇博文裏會繼續分析對於 CPU 實現指令的選取,以及選取後各個部件的控制信號選取,並提供核心示例代碼和整個系統的源碼。

  博客內所有文章均爲 原創,所有示意圖均爲 原創,若轉載請附原文鏈接。


二、設計過程

2.1 選取指令

  該 CPU 設計以實際的 RISC-V 指令系統 RV32I 爲基準,選取指令集六種指令格式中具有代表性的九條指令來進行實現,六種指令格式如下所示。
pic1
  其次選取的九條指令如下表所示:

格式 指令 操作
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

  示意圖如下:
pic2

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 邏輯代碼結構分析

pic3


3.5 仿真代碼結構分析

pic3

3.6 其餘文件分析

3.6.1 inst_rom.data 指令存儲器

inst_rom

3.6.2 data_rom.data 數據存儲器

data_rom

四、CPU 整體原理圖

pic4


五、源碼地址

  https://github.com/TIYangFan/CPU-Design-Based-on-RISC-V(如果可以幫到你,請幫忙 Star ~)

六、參考資料

  • 《計算機組成與設計(基於 RISC-V 架構)》—— 袁春風 餘子濠
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章