RISC-V学习笔记(五):RISC-V的功能实现详解之取指

前提

RISC-V实践部分我们使用的是三级流水,包含了取指、译码和执行三个步骤。

tinyriscv的流水线结构图:

1.取指

tinyriscv整体框架图:

对比tinyriscv的流水线结构图和tinyriscv整体框架图,取指实际涉及到的的只有pc_reg.v(PC寄存器)模块、rom.v(ROM)模块、if_id.v(流水线寄存器)模块,rib.v(RIB)模块是实行模块连接功能的,ctrl.v(控制单元)模块是实行产生暂停流水线、跳转等控制信号。

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

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

1.pc_reg.v(时序逻辑电路)

1.1功能

实现复位、跳转、暂停以及寻指

1.2输入输出信号
序号信号名输入/输出位宽(bits)说明
1clk输入1时钟输入信号
2

rst

输入1复位输入信号
3

jump_flag_i

输入1跳转标志,来源于ctrl.v的jump_flag
4

jump_addr_i

输入32跳转地址,跳转到该地址,来源于ctrl.v的jump_addr_o
5

hold_flag_i

输入3暂停标志,PC寄存器的值保持不变,来源于ctrl.v的hold_flag_o
6

jtag_reset_flag_i

输入1复位标志,设置为复位后的值,用在使用jtag下载时的复位reset_req_o
7

pc_o

输出

32

PC寄存器值,从该处取,输出到if_id.v的inst_addr_i
1.3复位

if (rst == `RstEnable || jtag_reset_flag_i == 1'b1) begin

            pc_o <= `CpuResetAddr;

注释:这个好像没啥讲的,就是复位信号来了就复位。

1.4跳转

end else if (jump_flag_i == `JumpEnable) begin

            pc_o <= jump_addr_i;

注释:跳转信号来了就跳转

1.5暂停

end else if (hold_flag_i >= `Hold_Pc) begin

            pc_o <= pc_o;

注释:暂停信号来了保持不变

1.6寻址(地址加4
CPU 取指令时,将 PC 的内容送到主存地址寄存器,然后修改PC 的值形成下一条将要执行的指令地址。

end else begin 

             pc_o <= pc_o + 4'h4;

注释:使用jal指令将下一条指令PC + 4 的地址保存到目标寄存器中,但这里我倾向于是使用了加法器,涉及到RISC-V寻址模式,一个字节是8bit数据,RV32为32bit数据也就是4个字节,所以读下一个32bit的指令就是 + 4字节。

2. if_id.v(时序逻辑电路)

2.1功能

指令向译码模块传递

2.2输入输出
序号信号名输入/输出位宽(bits)说明
1

clk

输入1时钟输入信号
2

rst

输入1复位输入信号
3inst_i输入32

输入指令内容,来源于rib.v的m0_data_o

4

inst_addr_i

输入32

输入指令地址,来源于pc_reg.v的pc_o

5

hold_flag_i

输入3

流水线暂停标志,来源 ctrl.v的hold_flag_o

6

int_flag_i

输入8

外设中断输入信号,来源连续赋值语句assign int_flag = {7'h0, timer0_int},timer0_int来源于timer.v的int_sig_o

7

int_flag_o

输出8

外设中断输出信号,输出到clint.v的int_flag_i

8

inst_o

输出32输出指令内容,输出到id.v的inst_i
9

inst_addr_o

输出32输出指令地址,输出到id.v的inst_addr_i

2.3 代码注释

gen_pipe_dff #(32) inst_ff(clk, rst, hold_en, `INST_NOP, inst_i, inst);

gen_pipe_dff #(32) inst_addr_ff(clk, rst, hold_en, `ZeroWord, inst_addr_i, inst_addr);

gen_pipe_dff #(8) inst_ff(clk, rst, hold_en, `INT_NONE, int_flag_i, int_flag);

注释:这三段话都是D触发器的例化,实现将输入的信号打一拍后再输出到译码模块(id.v)的功能。

3.rom.v(组合逻辑电路)

3.1功能 

存储指令码,格局PC寄存器的pc_o的值输出指令码

3.2输入输出
序号信号名输入/输出位宽(bits)说明
1

clk

输入1时钟输入
2rst输入1复位输入
3

we_i

输入1写使能,来自rib.v的s0_we_o
4

addr_i

输入32地址,来自rib.v的s0_addr_o
5

data_i

输入32输入的指令码,来自rib.v的s0_data_o
6

data_o

输出32输出的指令码,输出到rib.v的s0_data_i
3.3定义存储空间

reg[`MemBus] _rom[0:`RomNum - 1];

注释:定义一个32*4096的二维数组(我是理解为4096个32位宽的一个rom),用于做存储数据空间,一条指令32bit,也就是说这个rom可以存4096条指令。

3.4补充知识

RISC-V指令中的算术运算只作用在寄存器中,而数据传输指令就是为了在RISC-V的内存和寄存器间进行数据传输存在的。想访问内存中的字指令就需要提供内存地址。

注释:rom可以存4096个指令那它就有4096个地址 

3.5写数据

        if (we_i == `WriteEnable) begin

            _rom[addr_i[31:2]] <= data_i;

        end

注释:写使能就将指令码写入rom

3.6读数据 

if (rst == `RstEnable) begin

            data_o = `ZeroWord;

        end else begin

            data_o = _rom[addr_i[31:2]];

        end

注释:两种读方式,一种是当复位信号来时写出32'h0,另一种就是正常的读出指令码

3.7问题解答 

_rom[addr_i[31:2]]

addr_i为什么是[31:2]呢?

我的理解是因为它在pc_reg中pc_o的值 + 4后传入到rib.v进行处理后得到一个pc指针值是从addr_i[2]开始的 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值