处理器的指令集决定了处理器能做什么、擅长做什么,也决定了处理器的复杂度。举个例子,当指令集中包含了很多特殊数学运算指令如舍入,不同精度定点浮点运算,除法,蝶形运算和比特反转寻址等时,这个处理器一定是针对数字信号处理而设计的数字信号处理器。
RISC是精简指令集的缩写,其包含的指令都是最基本的,如算数运算,逻辑运算以及跳转等必须的指令。下图是一个典型的精简指令集。
这个指令集包含31条指令,如第三列所示。第一列是每种指令对应的操作码。由于这个指令集仅包含31条指令,操作码的位数是5位。为了便于扩充指令集,操作码编码为6位,最高位全是零。
操作数
通常每条指令有两个操作数(operand),分别为side_a和side_b。
有的指令只有一个操作数,如跳转指令,其唯一的操作数就是程序执行时要跳转到的地址。同样所有的移位操作也只有一个操作数,即暂存了待移位数的寄存器编号。移位后的数会仍然留在同一个寄存器中。
同样的,加一指令(INC)和减一指令(DEC)也只需要一个操作数,因为加减一后的数也留在原地。
需要注意的是有两个指令不需要任何操作数,分别是NOP指令和SLEEP指令。
NOP
NOP指令就是no operation的意思,处理器不干任何事情。那为什么需要这个指令?这个指令的用处可不小。程序中为了凑时序,等待,死循环等功能就是靠若干个NOP指令来实现的。
SLEEP
SLEEP睡眠指令也很重要,是为了让处理器临时进入低功耗状态从而实现节电的效果。SLEEP需要特殊的上电电路来实现。
指令长度
上表的Words列给出每种指令包含几个Words。每个Word包含16个bit。
可以看到除了加载立即数(LOADI)外其他所有指令都只占用一个Word,即16bit。这16比特中6bit是指令字(最高位为零,方便扩充指令集),5bit是操作数a对应的寄存
器的编号。这表示最大支持的寄存器数为32。最低5bit对应操作数b对应的寄存器。
指令周期
指令周期指一条指令执行时需要的时钟周期数。
三个加载和存储指令,加载立即数(LOADI)指令,从内存加载(LOAD)和存储(STORE)需要三个时钟周期来执行。其余所有指令都需要两个时钟周期来执行。
具体每个时钟周期处理器对应的任务分解随后会讲到。
进位标志 C-flag
进位标志表示当前运算是否发生了进位或者借位。如果发生了进位或者借位时,进位标志被置为1,否则进位标志被置为0。
上表C-flag列中标注x的指令被执行时会影响进位标志。
进位标志用来判断指令执行是否发生溢出,也可以用来级联前后操作,实现对大数的运算。
零标志 Z-flag
零标志表示当前运算结果是否为零。如果为零则该标志被置为1。
零标志可以用来判断预设的计数器是否减为零,两个数是否相等。
ALU 算术逻辑运算单元
ALU是处理器中具体完成每条指令运算的功能模块。上表最后三列给出了每条指令执行时每个时钟周期需要完成ALU运算。
经过分析归纳,ALU需要支持的运算包括对操作数加1,对操作数减1,两个操作数加和减,两个数的与或及异或等按位逻辑运算,操作数按位取反,左移右移。
ALU 的实现需要包括上面的所有功能。考虑到面积优化,加减1和减法可以全部由加反来实现。
ADD
以加法指令为例。第一个时钟周期时,ALU 被用来作程度计数器(PC program counter)的加1操作,从而使程序指针指向当前指令。第二个时钟周期完成实际的加法功能。
LOADI
所有指令中最特殊的指令是LOADI指令。由于LOADI是唯一的双字指令,第一个时钟周期时,ALU执行加1操作,将程序计数器指向该条指令的第一个16bit word,第二个时钟周期时,ALU再次执行加1操作,将程序计数器指向该指令的第二个word,即立即数。
这个经典的16bit精简指令处理器非常适合用来学习处理器设计,汇编语言学习,以及硬件描述语言学习。从理解指令集开始,到ALU设计到datapath 设计,大家会很快收到良好的学习效果。