本文为通过Verliog HDL编写简易RISC-V CPU的试验笔记,如有错误敬请指正
一.RISC-V
RISC-V为伯克利研究团队基于精简指令集计算原理建立的一个开放指令集架构。由于其开源、模型化及指令长度固定等特点而被应用于学习及嵌入式领域当中。
详见:什么是RISC-V-知乎
二.RISC-V指令
作为大型开源项目,RISC-V的官方技术文档可于Github上搜索得到并下载。此外,CSDN及知乎上亦有相关指令集介绍与表格可供参考,因此本文仅对RISC-V的指令架构作简略描述。
RISC-V指令结构
RISC-V指令包含数个主要结构,分别为访问寄存器的R-Type,使指指令中立即数进行操作的I-Type,储存至内存中的S-Type,判断分支执行的B-Type,执行跳转的J-Type,以及包含20位立即数的U-Type。
对于下方图表,opcode为指令操作码,rs1与rs2均为源寄存器地址,rd为目标寄存器地址,funct3及funct7为操作指令,imm为立即数,即计算机对该数据进行简易操作(扩展)后直接传递至计算单元进行运算的数据,其特点为数据被保存至指令中而无需访问寄存器。下表中imm[ ]表示数据的长度为位二进制编码,其顺序按高至低位排列,如11-0,20-0。
RV32指令集的指令位宽及数据位寛均为32位,寄存器地址长度为5位,最多可支持个寄存器,此外,由于指令不支持直接写入结果至内存,该指令集对寄存器的操作频率将较其他指令集高。
RV32I 基础整数指令集
RV32I Base Integer Instructions ( 32位基础整数指令集 )
RV32I主要包含37条运行指令与2条系统指令。相应的指令表格可通过RSIC-V Reference Card或RISC官方技朮文档得到。
1 | 指令按操作类型大致分为 |
详见:
RISC-V Reference Card - Github
RISC-V Instruction Set Manual - RISC-V International
寄存器的数学/逻辑运算指令
按R-Type结构进行编码,其指令操作码均为七位二进位数的(0110011),按funct3判断数据的运算类型。
以算朮加法ADD为例,其funct3为三位二进制的000,操作内容为从地址rs1及rs2对应的32位寄存器中取数据后通过算朮单元进行算朮加法,随后输出计算结果并储存至rd地址所对应的32位寄存器中。完成此流程则认为该指令执行完结。
下式为ADD指令的一个例子,该指令的rd为,rs1为,rs2为,funct3为,funct7为。
附注:
SUB(算朮减法)指令与ADD共用一个funct3值,需通过funct7进行判别。
SRA(循环右移)指令与SRL共用一个funct3值,需通过funct7进行判别。
SRA(循环右移)指令将数据的最右位循环至数据的的最高位。
SLT指令视数据为符号数进行比较,SLTU指令则视数据为无符号数进行比较。
立即数的数学/逻辑运算指令
按I-Type结构进行编码,其指令操作码均为七位二进位数的(0010011),按funct3判断数据的运算类型。
以算朮加法ADDI为例,其funct3为三位二进制的000,执行步骤为从地址rs1对应的32位寄存器中取数据,同时扩展立即数至32位数值,随后通过算朮单元进行算朮加法,完成后输出计算结果储存至地址rd所对应的32位寄存器中。完成此流程则认为该指令执行完结。
下式为ADDI指令的一个例子,其所对应的rd为,rs1为,funct3为。
附注:
一. 符号扩展:
立即数指令默认进行符号扩展。视所有立即数通过补码形式储存。若立即数最高位为1,则扩展时对其高20位补1。否则对高20位补0。
32位立即数扩展后的结构为`imm[31:0] = { imm[31:12], imm[11:0] }`
1 | if( imm[11] == 1 ) imm[31:12] = FFFFF; //高20位为1 |
二. 零扩展:
无论立即数的高位为何,其高位皆补零。
内存读取指令
按I-Type结构进行编码,其指令操作码均为七位二进位数的(0000011),按funct3判断数据的储存方式。
以字加载LW为例,其funct3为三位二进制的010,该指令先从地址rs1中取数据,与符号扩展的12位立即数进行符号算朮加法,并输出计算结果至32位地址总线,内存则通过地址总线上的值读出数据。并写入该数据至地址rd对应的寄存器。
1 | LW字加载: 读入四个字节M[31:0]至rd。 |
下式为LW指令的一个例子,其以rs1寄存器的值为基点偏移()后的内存位置读出一个字至rd寄存器中。
附注:
假设rs1中的值为32,则访问的内存位置为(32+(-24)) = 8,即rd = M[8][31:0]
上述表格未表示M的内存地址,完整的描述为M[rs1+imm][31:0]
内存储存指令
按S-Type结构进行编码,其指令操作码均为七位二进位数的(0100011),按funct3判断数据的储存方式。
以字加载SW为例,其funct3为三位二进制的010,该指令先从rs1中取数据,与符号扩展的12位立即数进行符号算朮加法,并输出计算结果至32位地址总线,指令同时取出rs2的值输出至数据总线。内存则通过地址总线及数据总线上的值。写入该数据至地址对应的区域。
1 | SW储存字: 写入四个字节rs2[31:0]至M[rs1+imm][31:0]。 |
下式为SW指令的一个例子,该指令将rs2的值写入以rs1为基点偏移()后的内存位置。
程序计数器的分支跳转
按B-Type结构进行编码,其指令操作码均为七位二进位数的(1100011),按funct3实施不同的跳转策略。
以大于跳转BGE为例,其funct3为三位二进制的101,该指令先从rs1与rs2中取数据,并判断rs1是否大等于于rs2,若真则修改PC的值为。若否则使。
下式为BGE指令的一个例子,若rs1值大于等于rs2,则PC跳转至
附注:
程序计数器( Program Counter ) - 百度百科
BLTU及BGEU对立即数进行零扩展而非符号扩展。
其他指令
由于所有RV32I指令均已在上表中给出说明,故其他指令不作描述。