二、RISC-V SoC内核注解——译码 代码讲解

tinyriscv这个SoC工程的内核cpu部分,采用经典的三级流水线结构进行设计,即大家所熟知的:取值—>译码—>执行三级流水线。

另外,在最后一个章节中会上传额外添加详细注释的工程代码,完全开源,如有需要可自行下载。

上一篇博文中注释了取值模块,现在来介绍译码模块:

目录

0 RISC-V SoC注解系列文章目录

1. 译码模块的整体介绍 

2. RISCV指令RV32I、RV32M介绍

3. 译码模块的注解

3.1 id.v(组合逻辑电路)

 3.2 id_ex.v(时序逻辑电路)


0 RISC-V SoC注解系列文章目录

零、RISC-V SoC软核笔记详解——前言

 一、RISC-V SoC内核注解——取指

 二、RISC-V SoC内核注解——译码

三、RISC-V SoC内核注解——执行

四、RISC-V SoC内核注解——除法(试商法)

五、RISC-V SoC内核注解——中断

六、RISC-V SoC内核注解——通用寄存器

七、RISC-V SoC内核注解——总线

八、RISC-V SoC外设注解——GPIO

九、RISC-V SoC外设注解——SPI接口

十、RISC-V SoC外设注解——timer定时器

十一、RISC-V SoC外设注解——UART模块(终篇)

1. 译码模块的整体介绍 

RISC-V内核的译码部分:涉及到id.v、id_ex.v、ctrl.v、clint.v、csr_reg.v等模块,其中clint.v、csr_reg.v两个模块是关于中断处理的模块,在中断章节会详细注解。ctrl.v模块是指令跳转和流水线暂停的控制信号,在后续的章节中会单独介绍。

译码部分详细结构图:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQ29kZV9jb3B5MQ==,size_20,color_FFFFFF,t_70,g_se,x_16

2. RISCV指令RV32I、RV32M介绍

这个RISCV软核,实现了RISCV基础指令集RV32I和扩展指令集RV32M(乘除法拓展指令)。关于这两个指令,在下文会一一标注出来。那具体是怎么实现指令的解析的呢?我们需要对照《RISC-V指令集手册》来进行注解。

《RISC-V指令集手册》的中文翻译版放在下边链接中了,需要的小伙伴可自行下载:

提取码:63bz

《RISC-V指令集手册》https://pan.baidu.com/s/1086s0N-BdVxUoZfYF5byRA首先对RV32I指令集进行简单的介绍,如果想了解更详细的内容请查看上述链接中的RISC-V指令集的中文参考手册《RISC-V-Reader-Chinese-v2p1》,第23页,第二章“RV32I:RISC-V基础整数指令集”:

RV32I是RISC-V基础整数指令集,它有六种基本指令格式,分别是:

  1. 用于寄存器-寄存器操作的 R 类型指令;
  2. 用于短立即数和访存 load 操作的 I 型指令;
  3. 用于访存 store 操作的 S 型指令;
  4. 用于条件跳转操作的 B 类型指令;
  5. 用于长立即数的 U 型指令;
  6. 用于无条件跳转的 J 型指令。

这六种指令的基本格式如下:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQ29kZV9jb3B5MQ==,size_20,color_FFFFFF,t_70,g_se,x_16

 RV32I所有指令的具体的指令布局,操作码,格式类型和名称的操作码映射如下图所示:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQ29kZV9jb3B5MQ==,size_13,color_FFFFFF,t_70,g_se,x_16

 这个RISC-V SoC工程完全实现了RV32I指令,因此上述表中的所有指令都会在译码模块中找到相应的代码。下文中会根据代码一一对应上述的指令。 

 接下来对扩展指令集RV32M(乘除法拓展指令)进行简单的介绍(详细介绍在中文参考手册《RISC-V-Reader-Chinese-v2p1》第51页)。

扩展指令集RV32M是32位整数的乘除法拓展指令:

乘法(指令的详细格式在中文参考手册152页):

1.mul指令将操作数寄存器rsl与rs2中的32位整数相乘,其64位结果的低32位写回寄存器rd中。两个32位整数操作数相乘的结果等于64位,而两个32位操作数当作有符号数相乘所得的低32位和当作无符号数相乘所得的低32位是相同的,因此RISC-V架构仅定义了一条mul指令作为取低32位结果的乘法指令。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQ29kZV9jb3B5MQ==,size_20,color_FFFFFF,t_70,g_se,x_16

 2.mulh指令将操作数寄存器rsl与rs2中的32位整数当作有符号数相乘,结果的高32位写回寄存器rd中。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQ29kZV9jb3B5MQ==,size_20,color_FFFFFF,t_70,g_se,x_16

 3.mulhu指令将操作数寄存器rsl与rs2中的32位整数当作无符号数相乘,结果的高32位写回寄存器rd中。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQ29kZV9jb3B5MQ==,size_20,color_FFFFFF,t_70,g_se,x_16

 4.mulhsu指令将操作数寄存器rsl与rs2中的32位整数相乘,其中rsl当作有符号数和rs2当作无符号数,将结果的高32位写回寄存器rd中。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQ29kZV9jb3B5MQ==,size_20,color_FFFFFF,t_70,g_se,x_16


3. 译码模块的注解

3.1 id.v(组合逻辑电路)

主要功能为:1.根据指令内容,解析出当前具体是哪一条指令(比如add指令);2.根据具体的指令,确定当前指令涉及的寄存器。比如读寄存器是一个还是两个,是否需要写寄存器以及写哪一个寄存器;3.访存:访问通用寄存器,得到要读的寄存器的值(地址)[1]。

输入信号:
// from if_id
    input wire[`InstBus]     inst_i,              // 指令内容 32位
    input wire[`InstAddrBus] inst_addr_i,         // 指令地址 32位
    // from regs
    input wire[`RegBus]      reg1_rdata_i,        // 通用寄存器1输入数据
    input wire[`RegBus]      reg2_rdata_i,        // 通用寄存器2输入数据
    // from csr reg
    input wire[`RegBus]      csr_rdata_i,         // CSR寄存器输入数据
    // from ex
    input wire               ex_jump_flag_i,      // 跳转标志
 
 //----------------------------以上输入----------以下输出---------------------------------
输出信号:
    // to regs
    output reg[`RegAddrBus ]  reg1_raddr_o,       // 读通用寄存器1地址
    output reg[`RegAddrBus ]  reg2_raddr_o,       // 读通用寄存器2地址
    // to csr
    output reg[`MemAddrBus ]  csr_raddr_o ,       // reg读CSR寄存器地址
    // to ex
    output reg[`MemAddrBus ]  op1_o       ,       //从指令中解析出来的操作数1  
    output reg[`MemAddrBus ]  op2_o       ,       //从指令中解析出来的操作数2
    output reg[`MemAddrBus ]  op1_jump_o  ,       //从指令中解析出来的跳转地址1
    output reg[`MemAddrBus ]  op2_jump_o  ,       //从指令中解析出来的跳转地址2
 
    output reg[`InstBus    ]  inst_o,             // 指令内容           与输入时内容相同
    output reg[`InstAddrBus]  inst_addr_o ,       // 指令地址           与输入时内容相同
    output reg[`RegBus     ]  reg1_rdata_o,       // 通用寄存器1数据    与输入时内容相同
    output reg[`RegBus     ]  reg2_rdata_o,       // 通用寄存器2数据    与输入时内容相同
                                                                       
    output reg                reg_we_o,           // 写通用寄存器标志  
    output reg[`RegAddrBus ]  reg_waddr_o,        // 写通用寄存器地址  
    output reg                csr_we_o,           // 写CSR寄存器标志   
    output reg[`RegBus     ]  csr_rdata_o,        // CSR寄存器数据      与输入时内容相同
    output reg[`MemAddrBus ]  csr_waddr_o         // 写CSR寄存器地址

首先将32位的指令码解析为6个部分,如下所示:

//将一条32位的指令分为6个部分,顺序如下
//funct7、rs2、rs1、funct3、rd、opcode
    wire[6:0] funct7 = inst_i[31:25];   //功能2,由funct7和funct3共同确定一条具体的指令
    wire[4:0] rs2    = inst_i[24:20];   //源寄存器2、立即数
    wire[4:0] rs1    = inst_i[19:15];   //源寄存器1、立即数
    wire[2:0] funct3 = inst_i[14:12];   //功能1,由funct7和funct3共同确定一条具体的指令
    wire[4:0] rd     = inst_i[11:7 ];   //目标寄存器、立即数
    wire[6:0] opcode = inst_i[6 :0 ];   //操作码,指出该指令需要完成操作的类型或性质;

接下来是组合逻辑实现译码操作,我们截取其中一部分内容进行注解,其余部分类似。

case (opcode)//先判断操作码  
   `INST_TYPE_I: begin      //短立即数和访存load操作   立即数类型  I type   
    case (funct3)  
        //当多个条件选项下需要执行相同的语句时,多个条件选项可以用逗号分开,放在同一个语句块的候选项中。        
        `INST_ADDI, `INST_SLTI, `INST_SLTIU, `INST_XORI, `INST_ORI, `INST_ANDI, `INST_SLLI, `INST_SRI: 
        begin//寄存器的值和立即数的值进行各种逻辑运算  
            reg_we_o = `WriteEnable; // 写通用寄存器标志     
            reg_waddr_o = rd;        //目标寄存器(写通用寄存器地址)  
            reg1_raddr_o = rs1;     //源寄存器1的地址,去通用寄存器模块中取reg1_rdata_i数据。  
            reg2_raddr_o = `ZeroReg;  
            op1_o = reg1_rdata_i;            //rs1地址从reg中取回的数据。  
            op2_o = {{20{inst_i[31]}}, inst_i[31:20]};  //指令中的立即数  
        end  
        default: begin  
            reg_we_o     = `WriteDisable;  
            reg_waddr_o  = `ZeroReg;  
            reg1_raddr_o = `ZeroReg;  
            reg2_raddr_o = `ZeroReg;  
        end  
    endcase  
end  

代码解析如下:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQ29kZV9jb3B5MQ==,size_20,color_FFFFFF,t_70,g_se,x_16

 由define.v文件可以查找到,宏定义INST_TYPE_I(opcode)的值是7'b0010011,INST_ADDI(func3)的值是3'b000,如下代码所示:

// I type inst
`define INST_TYPE_I 7'b0010011     
`define INST_ADDI   3'b000			//立即数+寄存器
`define INST_SLTI   3'b010			//寄存器<立即数->则为1
`define INST_SLTIU  3'b011			//寄存器比较立即数,比较时视为无符号数,如果小则写入1
`define INST_XORI   3'b100			//寄存器与立即数按位异或
`define INST_ORI    3'b110			//立即数|寄存器
`define INST_ANDI   3'b111			//立即数&寄存器
`define INST_SLLI   3'b001			//寄存器<<立即数
`define INST_SRI    3'b101			//寄存器>>立即数

 而操作码opcode,对应指令码的低7位[6:0],funct3对应指令码的[14:12]位:

wire[2:0] funct3 = inst_i[14:12];  //功能1
wire[6:0] opcode = inst_i[6 :0 ];  //操作码 

 即,inst_i[6 :0 ] = 7'b0010011,inst_i[14:12] = 3'b000,对应查找上文图2.3,可以找到唯一对应的指令如下所示:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQ29kZV9jb3B5MQ==,size_20,color_FFFFFF,t_70,g_se,x_16

 其余指令虽然格式可能会有多不同,按相同的方法都能找到相对应的指令描述。

 3.2 id_ex.v(时序逻辑电路)

主要功能:译码模块(id.v)的输出会送到id_ex模块的输入端,id_ex模块是一个时序电路,作用是将输入的信号打一拍后再输出到执行模块(ex.v)。通过例化gen_pipe_dff模块,如果该模块没有接收到复位或者暂停流水线信号hold_flag_in(来自Ctrl模块),则将输入的信号打一拍,输出至下一级。

输入信号:来自id模块

输出信号:输出到ex模块

    input wire clk,
    input wire rst,
 
    input wire[`InstBus] inst_i,            // 指令内容
    input wire[`InstAddrBus] inst_addr_i,   // 指令地址
    input wire reg_we_i,                    // 写通用寄存器标志
    input wire[`RegAddrBus] reg_waddr_i,    // 写通用寄存器地址
    input wire[`RegBus] reg1_rdata_i,       // 通用寄存器1读数据
    input wire[`RegBus] reg2_rdata_i,       // 通用寄存器2读数据
    input wire csr_we_i,                    // 写CSR寄存器标志
    input wire[`MemAddrBus] csr_waddr_i,    // 写CSR寄存器地址
    input wire[`RegBus] csr_rdata_i,        // CSR寄存器读数据
    input wire[`MemAddrBus] op1_i,
    input wire[`MemAddrBus] op2_i,
    input wire[`MemAddrBus] op1_jump_i,
    input wire[`MemAddrBus] op2_jump_i,
 
    input wire[`Hold_Flag_Bus] hold_flag_i, // 流水线暂停标志
 
    output wire[`MemAddrBus] op1_o,
    output wire[`MemAddrBus] op2_o,
    output wire[`MemAddrBus] op1_jump_o,
    output wire[`MemAddrBus] op2_jump_o,
    output wire[`InstBus] inst_o,            // 指令内容
    output wire[`InstAddrBus] inst_addr_o,   // 指令地址
    output wire reg_we_o,                    // 写通用寄存器标志
    output wire[`RegAddrBus] reg_waddr_o,    // 写通用寄存器地址
    output wire[`RegBus] reg1_rdata_o,       // 通用寄存器1读数据
    output wire[`RegBus] reg2_rdata_o,       // 通用寄存器2读数据
    output wire csr_we_o,                    // 写CSR寄存器标志
    output wire[`MemAddrBus] csr_waddr_o,    // 写CSR寄存器地址
    output wire[`RegBus] csr_rdata_o         // CSR寄存器读数据

  • 19
    点赞
  • 61
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小wang的IC自习室

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值