基于 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 架构)》—— 袁春风 余子濠
发布了277 篇原创文章 · 获赞 33 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_40697071/article/details/103353982
今日推荐