活动发起人@小虚竹 想对你说:
这是一个以写作博客为目的的创作活动,旨在鼓励大学生博主们挖掘自己的创作潜能,展现自己的写作才华。如果你是一位热爱写作的、想要展现自己创作才华的小伙伴,那么,快来参加吧!我们一起发掘写作的魅力,书写出属于我们的故事。我们诚挚邀请你参加为期14天的创作挑战赛!
提醒:在发布作品前,请将不需要的内容删除。
手把手教你搭建算术逻辑单元
算数逻辑单元, Arithmetic Logic Unit是处理器的核心部分,它负责对操作数进行算术和逻辑运算。本篇文章,将对ALU的内部构造经以及其工作原理经行深度剖析,其实ALU没有你想到这么复杂。此外,我会用提供ALU的Verilog代码,方便大家学习
1-Bit ALU
我们从零开始,循序渐进,从1bit逐渐过渡到64bit的ALU。
逻辑运算
1 bit的逻辑运算很简单,AND 还有 OR 运算如下图。
a和b分别接入到与门和或门,然后再通过选择器,选择输出的结果(Result)。
Result = (a·b) ·(!Operation)+(a+b)·Operation
算术运算
一位全加法器的框图抽象如下:
可以得出
CarryOut = (a+b)·CarryIn + a·b
Sum = a ^ b ^CarryIn
^为异或
于是有下图:
Sum同理,我们就不详细说明了
64-bit ALU
级联
1 比特的ALU通过简单的级联,如同串行进位加法器一样,就可以形成64 比特的ALU,如下:
功能扩展
1. Sub
在计算机中,补码计算
因为 A + !A = -1
所以 A - B = A + (! B )+ 1
因而做减法则需要对减数取反相加,然后再加1,这里刚好可以对输入进位置位
2. Nor
! ( a + b ) = ! a · !b
3. Slt
在RISC-V 架构中,需要支持 slt 指令的硬件连接
slt : set less than;
slt 指令结果与 rs1, rs2 的大小有关
如果 rs1 < rs2, 则结果的非最低位为0,最低位为1(set)
如果rs1>=rs2, 则结果的非最低位为0,最低为为0(reset)
要判断 a 和 b 的大小,我们可以前面减法的基础上,进行判断
如果 a < b , a - b <0 ; 那么输出结果为负,对应的符号位为1;
反之, 结果为非负,对应的符号位位 0
这里很有意思的巧合就是,结果的符号位刚好满足slt的操作要求
因而,我们可以将最高符号位,连接回最低位
**4.Zero **
在RISC-V 架构指令架构中,除了加减还有逻辑运算之外,我们还需要支持决策指令,如beq
beq r1, r2, THEN
如果 r1 = r2 , 跳转到THEN语句
否则,执行接下来的指令
那么该如何判断 r1 = r2 呢?
我们可以使用减法
( a - b = 0 )<=> a = b
那么可以有Zero信号
Zero = !(Result63 + Result62 +Result61+…Result1 + Result 0)
只用当所有的位的结果都相等(Resulti = 0, i=0,1,…63),即
Result63 + Result62 +Result61+…Result1 + Result0=0
Zero才为高
每次进行减法判断的时候,CarryIn会被置1,同时B会被取反(通过Binvert选择器选择),因而我们可以将 CarryIn 和 Binvert 简化为一个控制信号 Bnegate。
同时我们加入Overflow(溢出)检测
指令字段
那么ALU是如何与指令相连接起来的呢?
答案就藏在指令的指令的字段里面,我们在深入剖析RISC-V指令:类型、编解码与工作原理中有提到指令可以分成很多有效字段,它们决定了指令的类型和操作。
处理器在读取指令时,因为不同指令的操作和操作对象不一样,因而在操作的时候,处理器的控制单元会对funct段经行编码,与控制信号建立联系。控制信号对应的操作与ALU经行的功能如下:
段,它们决定了指令的类型和操作。
处理器在读取指令时,因为不同指令的操作和操作对象不一样,因而在操作的时候,处理器的控制单元会对funct段经行编码,与控制信号建立联系。控制信号对应的操作与ALU经行的功能如下:
funct字段和控制信号的具体联系和原因,不是本篇文章的重点,我们后面会提到。
代码
// ALU模块
module ALU(
input [3:0] ALUctrl,
input [63:0] A,
input [63:0] B,
output reg [63:0] ALUOut,
output Zero
);
assign Zero = (ALUOut == 0); // 零标志生成
always @(*) begin // 使用组合逻辑敏感列表
case (ALUctrl)
4'b0000: ALUOut = A & B; // AND
4'b0001: ALUOut = A | B; // OR
4'b0010: ALUOut = A + B; // ADD
4'b0110: ALUOut = A - B; // SUB
4'b0111: ALUOut = (A < B) ? 64'd1 : 64'd0; // SLT
4'b1100: ALUOut = ~(A | B); // NOR
default: ALUOut = 64'd0; // 默认安全值
endcase
end
endmodule
// 修改后的ALU控制模块
module ALUControl(
input [1:0] ALUOp,
input [5:0] FuncCode,
output reg [3:0] ALUCtrl
);
//整个RSIC-V 有很多类型的指令,此处只做一些常见的指令的编码;
always @(*) begin
case (ALUOp)
2'b00: ALUCtrl = 4'b0010; // 加法(用于lw/sw/addi)
2'b01: ALUCtrl = 4'b0110; // 减法(用于beq)
2'b10: begin // R-type指令
case (FuncCode)
6'b10_0000: ALUCtrl = 4'b0010; // ADD
6'b10_0010: ALUCtrl = 4'b0110; // SUB
6'b10_0100: ALUCtrl = 4'b0000; // AND
6'b10_0101: ALUCtrl = 4'b0001; // OR
6'b10_0111: ALUCtrl = 4'b1100; // NOR
6'b10_1010: ALUCtrl = 4'b0111; // SLT
default: ALUCtrl = 4'b1111; // 未知操作
endcase
end
default: ALUCtrl = 4'b1111; // 异常操作码处理
endcase
end
endmodule