六、RISC-V SoC内核——通用寄存器 代码讲解

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

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

上一篇博文中注释了中断模块,现在来介绍通用寄存器reg.v模块:

目录

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

1. reg在内核中的位置

2. RISC-V通用寄存器

3. reg.v

3.1 输入输出接口:

3.2. 主要功能

3.3. 代码注释

参考:


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. reg在内核中的位置

如下图,绿色的方块是通用寄存器reg,从位置上可以看出,通用寄存器在功能上,主要承担译码和执行部分的临时数据存储:

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

2RISC-V通用寄存器

RISC-V架构支持32位或者64位的架构,32 位架构由RV32表示,其每个通用寄存器的宽度为32比特; 64位架构由RV64表示,其每个通用寄存器的宽度为64比特。

RISC-V架构的整数通用寄存器组,包含32个(I 架构)或者16个(E架构)通用整数寄存器,其中整数寄存器0被预留为常数0,其他的31个(I架构)或者15个(E架构)为普通的通用整数寄存器。

如果使用浮点模块(F或者D),则需要另外一个独立的浮点寄存器组,包含32个通用浮点寄存器。如果仅使用F模块的浮点指令子集,则每个通用浮点寄存器的宽度为32比特;如果使用了D模块的浮点指令子集,则每个通用浮点寄存器的宽度为64比特。

在流水线中能够尽快地读取通用寄存器组,往往是处理器流水线设计的期望之一,这样可以提高处理器性能和优化时序。这个看似简单的道理在很多现存的商用RISC架构中都难以实现,因为经过多年反复修改不断添加新指令后,其指令编码中的寄存器索引位置变得非常凌乱,给译码器造成了负担。

得益于后发优势和总结了多年来处理器发展的经验,RISC-V的指令集编码非常规整,指令所需的通用寄存器索引都被放在固定的位置,如下所示。因此指令译码器可以非常便捷地译码出寄存器索引,然后读取通用寄存器组。

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

RV32I 规整的指令编码格式

3. reg.v

3.1 输入输出接口:

input wire clk,  
input wire rst,  

// from ex  
input wire we_i,                      // 写寄存器标志  
input wire[`RegAddrBus] waddr_i,      // 写寄存器地址  
input wire[`RegBus] wdata_i,          // 写寄存器数据  
  
// from jtag  
input wire jtag_we_i,                 // 写寄存器标志  
input wire[`RegAddrBus] jtag_addr_i,  // 读、写寄存器地址  
input wire[`RegBus] jtag_data_i,      // 写寄存器数据  
  
// from id  
input wire[`RegAddrBus] raddr1_i,     // 读寄存器1地址  
  
// to id  
output reg[`RegBus] rdata1_o,         // 读寄存器1数据  
  
// from id  
input wire[`RegAddrBus] raddr2_i,     // 读寄存器2地址  
  
// to id  
output reg[`RegBus] rdata2_o,         // 读寄存器2数据  
  
// to jtag  
output reg[`RegBus] jtag_data_o       // 读寄存器数据 

注意,上述代码注释中的的寄存器1不是指x1寄存器,寄存器2也不是指x2寄存器。而是指一条指令里涉及到的两个寄存器(源寄存器1和源寄存器2)。一条指令可能会同时读取两个寄存器的值,所以有两个读端口。又因为jtag模块也会进行寄存器的读操作,所以一共有三个读端口。

3.2. 主要功能

程序中定义了一个宽度位32位,深度位32位的寄存器regs。

reg[`RegBus] regs[0:`RegNum - 1]; //32*32 //reg[31:0]位宽 regs[0:31] 深度

1.写寄存器:将ex或者jtag中的数据,寄存在寄存器regs中;

2.读寄存器(组合逻辑):

        读寄存器的地址来自译码id模块,并将从寄存器中读到的数据,送给译码id模块(regs)。

        读寄存器的地址来自jtag模块,并将从寄存器中读到的数据,送给jtag模块(jtag读寄存器)。

3.3. 代码注释

// 写寄存器 写寄存器操作来自执行模块。  
always @ (posedge clk) begin  
    if (rst == `RstDisable) begin//`define RstDisable 1'b1  
        // 优先ex模块写操作 写使能为高,并且写寄存器地址不为0时,因为寄存器x0是只读寄存器并且其值固定为0。  
        if ((we_i == `WriteEnable) && (waddr_i != `ZeroReg)) begin  
            //将ex模块中的写数据和地址,写入寄存器相应的地址当中  
            regs[waddr_i] <= wdata_i;  
            //否则当 jtag的写使能为高,并且写寄存器地址不为0时,因为寄存器x0是只读寄存器并且其值固定为0。  
        end else if ((jtag_we_i == `WriteEnable) && (jtag_addr_i != `ZeroReg)) begin  
            //将jtag模块中的写数据和地址,写入寄存器相应的地址当中  
            regs[jtag_addr_i] <= jtag_data_i;  
        end  
    end  
end  
  
//读寄存器    
// 读寄存器1  读寄存器操作来自译码模块,并且读出来的寄存器数据也会返回给译码模块。  
//assign rdata1_o = (raddr1_i == `ZeroReg) ? `ZeroWord : ((raddr1_i == waddr_i && we_i == `WriteEnable) ? wdata_i : regs[raddr1_i]);   
always @ (*) begin  
    if (raddr1_i == `ZeroReg) begin //如果读地址为0,因为寄存器x0是只读寄存器并且其值固定为0。   
        rdata1_o = `ZeroWord;//因此输出数据为32'h0  
        //第二条指令依赖于第一条指令的结果。为了解决这个数据相关的问题。  
        //如果读地址等于写地址,并且正在写操作,则直接将要写的值返回给读操作。  
    end else if (raddr1_i == waddr_i && we_i == `WriteEnable) begin  
        rdata1_o = wdata_i;  
    end else begin  //如果没有数据相关,则返回要读的寄存器的值。  
        rdata1_o = regs[raddr1_i];  
    end  
end  

注意:由于流水线的原因,当前指令处于执行阶段的时候,下一条指令则处于译码阶段。由于执行阶段不会写寄存器,而是在下一个时钟到来时才会进行寄存器写操作,如果译码阶段的指令需要上一条指令的结果,那么此时读到的寄存器的值是错误的。比如下面这两条指令:add x1, x2, x3  、add x4, x1, x5  第二条指令依赖于第一条指令的结果。为了解决这个问题,如果读寄存器等于写寄存器,则直接将要写的值返回给读操作。

// 读寄存器2 读寄存器操作来自译码模块,并且读出来的寄存器数据也会返回给译码模块。  
//assign rdata2_o = (raddr2_i == `ZeroReg) ? `ZeroWord : ((raddr2_i == waddr_i && we_i == `WriteEnable) ? wdata_i : regs[raddr2_i]);   
always @ (*) begin  
    if (raddr2_i == `ZeroReg) begin  
        rdata2_o = `ZeroWord;  
    //如果读地址等于写地址,并且正在写操作,则直接返回写数据  
    end else if (raddr2_i == waddr_i && we_i == `WriteEnable) begin  
        rdata2_o = wdata_i;  
    end else begin  
        rdata2_o = regs[raddr2_i];  
    end  
end  
  
// jtag读寄存器  
//assign jtag_data_o = (jtag_addr_i == `ZeroReg) ? `ZeroWord : regs[jtag_addr_i];   
always @ (*) begin  
    if (jtag_addr_i == `ZeroReg) begin  
        jtag_data_o = `ZeroWord;  
    end else begin  
        jtag_data_o = regs[jtag_addr_i];  
    end  
end  
  • 5
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小wang的IC自习室

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

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

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

打赏作者

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

抵扣说明:

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

余额充值