打印
[RISC-V MCU 应用开发]

从零开始写RISC-V处理器(硬件篇二)

[复制链接]
1066|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主

4.4  取指

目前tinyriscv所有外设(包括rom和ram)、寄存器的读取都是与时钟无关的,或者说所有外设、寄存器的读取采用的是组合逻辑的方式。这一点非常重要!

tinyriscv并没有具体的取指模块和代码。PC寄存器模块的输出pc_o会连接到外设rom模块的地址输入,又由于rom的读取是组合逻辑,因此每一个时钟上升沿到来之前(时序是满足要求的),从rom输出的指令已经稳定在if_id模块的输入,当时钟上升沿到来时指令就会输出到id模块。

取到的指令和指令地址会输入到if_id模块(if_id.v),if_id模块是一个时序电路,作用是将输入的信号打一拍后再输出到译码(id.v)模块。

.

4.5译码

译码模块所在的源文件:rtl/core/id.v

译码(id)模块是一个纯组合逻辑电路,主要作用有以下几点:

1.根据指令内容,解析出当前具体是哪一条指令(比如add指令)。

2.根据具体的指令,确定当前指令涉及的寄存器。比如读寄存器是一个还是两个,是否需要写寄存器以及写哪一个寄存器。

3.访问通用寄存器,得到要读的寄存器的值。

译码模块的输入输出信号如下表所示:

以add指令为例来说明如何译码。下图是add指令的编码格式:

可知,add指令被编码成6部分内容。通过第1、4、6这三部分可以唯一确定当前指令是否是add指令。知道是add指令之后,就可以知道add指令需要读两个通用寄存器(rs1和rs2)和写一个通用寄存器(rd)。下面看具体的代码:


第1行,opcode就是指令编码中的第6部分内容。

第3行,`INST_TYPE_R_M的值为7’b0110011。

第4行,funct7是指指令编码中的第1部分内容。

第5行,funct3是指指令编码中的第4部分内容。

第6行,到了这里,第1、4、6这三部分已经译码完毕,已经可以确定当前指令是add指令了。

第7行,设置写寄存器标志为1,表示执行模块结束后的下一个时钟需要写寄存器。

第8行,设置写寄存器地址为rd,rd的值为指令编码里的第5部分内容。

第9行,设置读寄存器1的地址为rs1,rs1的值为指令编码里的第3部分内容。

第10行,设置读寄存器2的地址为rs2,rs2的值为指令编码里的第2部分内容。

其他指令的译码过程是类似的,这里就不重复了。译码模块看起来代码很多,但是大部分代码都是类似的。

译码模块还有个作用是当指令为加载内存指令(比如lw等)时,向总线发出请求访问内存的信号。这部分内容将在总线一节再分析。

译码模块的输出会送到id_ex模块(id_ex.v)的输入,id_ex模块是一个时序电路,作用是将输入的信号打一拍后再输出到执行模块(ex.v)。

.

4.6  执行

执行模块所在的源文件:rtl/core/ex.v

执行(ex)模块是一个纯组合逻辑电路,主要作用有以下几点:

1.根据当前是什么指令执行对应的操作,比如add指令,则将寄存器1的值和寄存器2的值相加。

2.如果是内存加载指令,则读取对应地址的内存数据。

3.如果是跳转指令,则发出跳转信号。

执行模块的输入输出信号如下表所示:

下面以add指令为例说明,add指令的作用就是将寄存器1的值和寄存器2的值相加,最后将结果写入目的寄存器。代码如下:



第2~4行,译码操作。

第5行,对add或sub指令进行处理。

第6~12行,当前指令不涉及到的操作(比如跳转、写内存等)需要将其置回默认值。

第13行,指令编码中的第30位区分是add指令还是sub指令。0表示add指令,1表示sub指令。

第14行,执行加法操作。

第16行,执行减法操作。

其他指令的执行是类似的,需要注意的是没有涉及的信号要将其置为默认值,if和case情况要写全,避免产生锁存器。

下面以beq指令说明跳转指令的执行。beq指令的编码如下:

beq指令的作用就是当寄存器1的值和寄存器2的值相等时发生跳转,跳转的目的地址为当前指令的地址加上符号扩展的imm的值。具体代码如下:

第2~4行,译码出beq指令。

第5~10行,没有涉及的信号置为默认值。

第11行,判断寄存器1的值是否等于寄存器2的值。

第12行,跳转使能,即发生跳转。

第13行,计算出跳转的目的地址。

第15、16行,不发生跳转。

其他跳转指令的执行是类似的,这里就不再重复了。



使用特权

评论回复

相关帖子

发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

78

主题

124

帖子

0

粉丝