本文章参考书籍:Digital Design and Computer Architecture RISC-V Edition
本文章仅介绍了几种简单指令,详细信息可以参考书籍或者RISC-V官方网站。
1. 指令
一个最常用的计算机操作是加法,下图给出了高级语言和RISC-V汇编语言(以下都简称为汇编语言)描述加法的对比:
汇编语言中的add是助记符,便于我们明白是何操作。操作在b和c上执行,b,c被成为源操作数(sourse operands)。结果被写入a中,a被称为目标操作数(destination operands)。减法也和加法类似:
这两种操作都是两个源操作数和一个目标操作数。而对于一个更复杂的例子是(即包括加法又包括减法):
高级语言可以一行写完,但是对于汇编语言来说,只能分步进行。先进行加法操作,并且用中间变量暂存结果,再进行减法,得到最后的结果。
2.操作数
前面我们提到了操作数的概念,并根据指令将其分成了源操作数和目标操作数。但是对操作数而言,其存储位置更为重要,决定了我们到什么位置取值。根据存储位置我们可以将操作数分为:寄存器操作数、常数和存储器操作数。
2.1寄存器操作数
指令想要执行得快就要快速取到操作数的值,而取寄存器里的值比取存储器里的值要快很多。因此我们把一些经常需要用的数存储在寄存器里面。RISC-V架构有32个寄存器,称为寄存器集,存储在一个小的多端口内存中,称为寄存器文件。还是前面的例子:
这里a,b,c,d分为存储在s0,s1,s2,s3中,中间值t也存储在寄存器中。s0,s1,s2,s3,t0表示寄存器集中的寄存器。寄存器集中的寄存器按其作用进行了区分,如下表所示:
这些如果我们经常写汇编语言的话,大概都能记住。
2.2常数/立即数
因为常数不用到寄存器或者存储器中取值,立即就能使用,所以也被成为立即数。下图展示了立即数的例子:
addi表示将立即数加到计算器操作数上。立即数的写法可以是二进制,十进制或者十六进制。标出0x或者0b即可。如上图第二个例子所示,addi也可以用来对参数进行初始化。立即数是12位二进制的补码数,因此它们是符号扩展到32位的。要创建较大的立即数,可以使用load upper immediate指令(lui)和add immediate指令(addi)。如下图所示:
lui意思是加载高位立即数。这里lui加载了20位立即数作为高二十位,而剩下的位数都置零。然后再加上12bit的立即数。前面说到立即数是符号位扩展到32位的,因此当立即数为负数时,需要将符号位都扩展为1,例如:
这里先将0xFEEDB存入寄存器s2,作为高20位(所需的高位0xREEDA加上1)。0x987的十进制为-1657(最高位为符号位)。因此addi s2,s2,-1657实际上是:0xFEEDB000 + 0xFFFFF987 = 0xFEEDA987。
3.存储器操作数
寄存器存储的数据量小,但是速度快。存储器存储的数据量大,但是速度慢。在RISC-V架构中,指令都是在寄存器中执行的,所以数据需要先移动到寄存器中才能进行接下的操作。
RISC-VS使用字节地址存储器,意思是每一个字节拥有一个独特的地址。如下图所示:
一个32-bit的word包含四个字节,因此每一个word地址都为4的倍数。例如这里的0xF2F1AC07是存储在地址00000004处。
我们可以使用lw(load word)指令来将存储器中的数据(一个word)加载到寄存器中。例如:
lw指令中说明地址的方式是偏移量加基地址。这里mem[2]表示word2,而word2在存储器中的地址为00000008,因此例子中的语句表示将存储器中地址为8的数据加载到s7寄存器中。sw(store word)指令是将寄存器中的数据移动到存储器中,地址的表示方式和lw指令相同。例如: