本文章参考书籍:Digital Design and Computer Architecture RISC-V Edition
这一节内容比较多,打算分成几篇来写。我的目标是从零使用verilog来构建一个RISC-V核的CPU。汇编语言能够帮我们更好的理解架构,但是不是主线任务。因此这一节并不一定会覆盖得全面,可能先介绍一些基础指令打一下底子。后续使用verilog来构建cpu时,如果需要进一步理解,再更新这一节。
在高级语言中,我们通常用 if/else结构,循环结构等来实现一个项目。这些高级语言在软件中编写之后,有的软件会转化成汇编语言。本文章将会讨论一个高级结构如何用汇编语言来实现。
1.程序流程
和数据一样,指令也被存在存储器中,RISC-V架构中的指令为32bit,四个字节。因此连续的指令之间地址递增为4。例如:
addi s1,s2,s3存在0x538,其余两条指令也存储在对应位置,相邻两指令的地址差为4。当我们想知道当前指令的地址时,使用
p
r
o
g
r
a
m
−
c
o
u
n
t
e
r
program-counter
program−counter (PC)来追踪指令地址。每执行完一个指令,PC就增加4。例如:addi执行时,PC=0x538,执行完之后,PC=0x53C,增加了4。
2. 逻辑和移位指令
2.1 逻辑指令
RISC-V的逻辑指令包括 and,or,xor。这些指令都包括两个源操作数和一个目标操作数。比如and s3 ,s1,s2。s3为目标操作数,s1,s2为源操作数,第一个为目标操作数。例子如下:
and 操作在作为清楚很掩码时非常有用,如上图s1的低16位可以通过and操作全部清零。而高16bit可以保持不变。or 操作可以用于置位或者组合两个寄存器的位域。针对立即数也有同样的操作:andi,ori和xori。我们可以利用立即数来实现很多功能,例如实现NOT操作,我们可以写指令:xori s8, s1, −1。-1的补码是0xFFF,进过符号位扩展到32bit,实际上是0xFFFFFFF。通过XOR操作,可以实现按位翻转。
2.2 移位指令
移位分为逻辑移位和算术移位,其中逻辑左移和算术左移是相同的,因此RISC-V中提供了三种移位指令:sll(shift left logic),srl(shift right logic)和 sra(shift right arithmetic)。算术右移和逻辑右移的区别在于算术右移需要保持符号位不变,因此高位需要补充符号位,而不是补零。这三种操作同样有立即数版本:slli,srli和 srai。位移量是5bit(表示2的五次方个数)的无符号数。移位指令中,目标操作数和源操作数的位置和逻辑指令一致。写法如下:
左移相当于乘2
N
^N
N,右移相当于除2
N
^N
N。这个大家应该都知道。其次逻辑移位针对无符号数,算术移位针对的是二进制补码。
3.分支指令
如果程序只能顺序执行,那会变得很无聊。我们需要计算机做决定,因此就有了if/else,case等选择语句以及for,while等循环语句。分支指令修改程序的流程,以便处理器可以获取内存中不按顺序排列的指令。分支指令包括条件分支,以及无条件分支。
3.1条件分支
RISC-V指令集中有六个条件分支指令:beq(branch if equal), bne(branch if not equal), blt(branch if less than), bge(branch if greater than or equal to), bltu and bgeu。bltu和bgeu的操作数为无符号数,u表示unsigned,blt和bge则针对有符号数。每个都有两个源寄存器和一个指示去哪里的标签。下图是beq指令的例子:
上图中beq s0,s1,target表示当寄存器s0中的数和寄存器s1中的数相等时,则跳转到target处往下执行指令。RISC-V汇编语言中用label来指示指令的位置。这里的target就是label。如果不相等则继续顺序执行。将beq改成bne,就变成了条件不满足的情况,如下图:
需要注意的是最后一条语句,add s1,s1,s0这条语句也执行了。没有跳转的情况下,程序就会一直顺序执行。
3.2无条件分支——跳转(jump)
跳转有三条指令:jump(j), jump and link(jal), and jump register(jr)。j 表示程序直接跳转到label处。下图展示了j指令的例子:
jal指令和jr指令频繁用于构建函数,后续会在函数模块介绍这两条指令。