十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

四位ALU算术逻辑单元设计实验:Verilog实现与标志位解析

四位ALU算术逻辑单元设计实验:Verilog实现与标志位解析 简介四位ALU算术逻辑单元设计实验以Word文档形式提供面向计算机组成原理、数字逻辑课程的学生及相关实验人员用于解决四位ALU功能设计、超前进位加法器实现和级联扩展等核心问题。包内仅含1个docx文件压缩包709KB内容编排完整、图表配套。文档从实验目的出发给出八种功能操作表通过卡诺图推导控制信号与输入输出的逻辑关系并配有函数发生器、全加器及超前进位链的电路设计同时将四级级联控制端G的处理方式一并说明。其中也展示了MAXPLUS Ⅱ下的封装、仿真与单级测试结果最后还记录了常见软件冲突与调试心得。目前已有544人学习下载适合需要对照完成实验报告、理解ALU内部结构或准备相关考试的读者参考读者可据此复现电路设计、核对仿真结果或直接借鉴实验报告框架。 看到“四位ALU算术逻辑单元设计实验”这个题目我最开始觉得没什么可做的——不就是把A和B送进去按功能码选一种运算输出吗直到自己动手设计完才发现里面藏着CPU设计里最核心的几个问题功能码怎么编码才合理加法和减法能不能共用一套电路标志位到底该怎么判断才不会有坑这个实验做完你对数字逻辑和计算机组成原理的理解会明显上一个大台阶它特别适合正在学这两门课的本科生也适合想自己写一个小CPU的爱好者。写这篇文章的目的就是把我实际跑通的设计方案、完整代码、仿真测试和踩过的坑一次性说清楚。我不会只给一个成品而是把每一步的“为什么”也讲透。这样你拿到手不仅能照着复现还能在实验报告里把设计思路写明白。1. 先聊清楚这个四位ALU实验到底在做什么1.1 一条计算指令背后发生了什么ALUArithmetic Logic Unit算术逻辑单元的本质就是一个可以根据外部控制信号从多种操作中选一种执行的组合逻辑电路。要理解它在CPU里的位置可以看一条简单指令的完整路径CPU从存储器取到指令译码器判断出这是一条加法指令然后把寄存器里的两个操作数送到ALU输入端同时把功能选择信号也送到ALU的F端口。ALU完成计算后把结果写回寄存器同时输出Zero、Carry、Overflow这些标志位供后续分支跳转判断使用。这里最容易忽略的一点是指令本身不会直接告诉ALU“你要执行加法”而是通过译码后的一组控制信号来驱动ALU。换句话说ALU并不“认识”指令它只认识功能码。这种分层设计思想是整个CPU设计的基石。搞清楚这一点你做实验时就不会困惑“为什么功能码要这么设计”了。1.2 相同实验三种完全不同的实现路线我实际列过三种方案每种都有明确的使用场景实现路线优点缺点适合场景真值表查询式逻辑最简单列出输入输出对应关系即可4位ALU输入过多真值表规模指数爆炸只做1位演示门级电路/Logisim能看到每个门电路直观理解硬件结构连线复杂四位已经眼花改功能等于重画教学演示、逻辑化简训练行为级Verilog 仿真描述功能而非门级网表代码短、易修改容易脱离硬件细节需要仿真验证课程实验、FPGA设计、工程实践我给课程实验的建议是如果题目没有强制指定用Logisim优先选第3种。行为级Verilog写起来更接近“设计者思考问题的方式”而且四位ALU的规模正好适合用Verilog描述还能顺便把testbench写作练了。之后的FPGA上板验证也完全不需要换方案。2. 需求拆解与方案选型为什么要这么设计ALU2.1 支持哪些运算才算“够用”根据标题要求和实际用途我给这个四位ALU定了8种运算与、或、异或、或非、加、减、比较SLT、PASS A。用3位功能码F[2:0]正好从000编码到111。这个集合不是随便定的。加减覆盖算术核心功能逻辑运算覆盖按位操作SLTSet Less Than用于比较大小PASS A则用于数据通路中让某一路数据直接通过。如果你做的是课程实验运算集合可以按题目要求增减但我强烈建议至少包含ADD和SUB——这两个功能最能暴露进位、借位和溢出标志的设计细节也是老师最喜欢在答辩时追问的地方。2.2 功能码怎么编码更顺手这里我参考了成熟指令集里ALU控制信号的编码习惯设计成下面这张表F[2:0]运算说明000Y A B按位与001Y A | B按位或010Y A ^ B按位异或011Y ~(A | B)按位或非100Y A B Cin带进位加法101Y A (~B) 1A减B110Y (A B) ? 1 : 0比较111Y A传A编码设计时我有个小讲究把逻辑运算放在低段算术运算放在高段。这样做的好处是如果你后续在做指令译码可以直接用功能码的某一位来控制是否走加法器通路而不需要额外译码。这种“用功能码里的bit直接驱动硬件选择”的思路在实际指令集设计里非常常见。2.3 行为级Verilog为什么胜出很多人觉得写Verilog就一定要写门级描述其实这是一个误区。现代FPGA开发流程中综合工具会把行为级的case语句自动映射成LUT和MUX而不是真的按你写的if/else一行一行生成门电路。你的任务是定义清楚功能、设计好数据通路优化逻辑交给综合工具就行。行为级Verilog对实验来说最大的优势是代码短、改起来快、仿真直观。你可以把全部注意力放在ALU设计逻辑本身上而不是浪费在检查连线上。四位ALU这种规模用行为级描述是最舒服的代码不会太短到没意义也不会长到难以调试。3. 核心细节解析减法、进位、溢出三个必考重点3.1 减法复用加法器A-BA(~B)1ALU里不需要单独设计一个减法器这是所有教科书都会强调的经典设计。减法的本质是补码运算A减去B等于A加上B的补码。B的补码就是B按位取反再加1。所以在实现时减法功能码走的还是那条加法通路只是B的输入从B变成了~B同时多加了1。那个额外的“1”从哪里来可以在加法器的最低位进位输入端想办法也可以像我后面代码里那样直接写 1b1。如果题目要求支持带借位输入需要把加法器的Cin和减法逻辑联动处理这会复杂不少。课程实验一般不需要支持带借位减法所以我选择了简化方案。3.2 标志位设计Zero、Carry、Overflow怎么算才正确这是实验报告里最容易写错的部分我逐个说。Zero最简单结果Y的所有位都为0Z就为1代码里直接判断Y 4b0即可。Carry进位/借位有一个约定问题。对无符号加法最高位产生的进位就是C对减法很多教材约定“无借位时C1有借位时C0”和x86体系里的CF标志一致。我在代码里减法也按这个约定实现C ~借位。写实验报告前一定要先明确你采用的是哪种约定否则答辩时很容易被问住。Overflow是最大的坑。无符号数和有符号数的溢出判断完全不一样对四位补码有符号数范围是-8到7。两个正数相加超过7或者两个负数相加小于-8就发生了溢出。硬件上最常用的判断方法是“最高位进位异或次高位进位”这个结论对任意位宽都成立而且实现起来只需要一个异或门非常漂亮。3.3 比较运算SLT的实现SLT功能可以看成减法功能的副产品计算A减B如果结果为负说明A小于B。但这里有个细节必须注意直接判断减法结果符号位最高位不一定可靠因为溢出会干扰符号位的意义。稳妥做法是判断符号位和溢出标志的组合。不过我在实际代码里用的是A B这个比较表达式它本质上是综合工具帮我做了比较电路简洁且直观。但如果你要在实验报告里讲清楚原理建议自己写一次tmp A (~B) 1再分析结果。另外还要提前定义清楚A和B是按无符号数比较还是按有符号数比较。对四位二进制来说无符号的1111是15有符号的1111是-1同样一组输入比较结果完全相反。4. 完整代码与仿真验证直接能跑的版本4.1 Verilog实现这是我在实验里实际跑通并且综合通过的版本没有任何多余的封装直接可以复制到工程里用。module alu_4bit ( input [3:0] A, input [3:0] B, input [2:0] F, input Cin, output reg [3:0] Y, output reg Zero, output reg CarryOut, output reg Overflow ); reg [4:0] tmp; always (*) begin // 默认值避免产生锁存器 tmp 5b0; Y 4b0; Zero 1b0; CarryOut 1b0; Overflow 1b0; case (F) 3b000: Y A B; // AND 3b001: Y A | B; // OR 3b010: Y A ^ B; // XOR 3b011: Y ~(A | B); // NOR 3b100: begin // ADD tmp A B Cin; Y tmp[3:0]; CarryOut tmp[4]; Overflow (A[3] B[3]) (Y[3] ! A[3]); end 3b101: begin // SUB tmp A (~B) 1b1; Y tmp[3:0]; CarryOut ~tmp[4]; // 无借位时C1 Overflow (A[3] ! B[3]) (Y[3] ! A[3]); end 3b110: Y (A B) ? 4b0001 : 4b0000; // SLT 3b111: Y A; // PASS A endcase Zero (Y 4b0); end endmodule这里有几个坑必须提醒你tmp必须声明成5位reg否则AB产生的进位会被截断CarryOut永远算不出来。这是新手最容易踩的坑。always块里每个分支我都赋值了但还是在开头给了默认值。原因很简单如果case没有覆盖所有分支Verilog会综合出锁存器latch这种隐式latch在组合逻辑里非常危险仿真时不容易发现上板后问题才暴露。减法里我用的是1b1而不是外部Cin这是简化设计。如果题目要求支持带借位的减法需要把这里的常数换成Cin同时仔细处理借位极性。4.2 Testbench怎么设计才全面很多同学的测试就是随便挑几个数算一算看到结果好像对就收工了。这样远远不够。我写的testbench覆盖了三个层面基本逻辑运算、带标志位的算术运算、边界情况比如溢出和全零结果。timescale 1ns/1ps module tb_alu_4bit; reg [3:0] A, B; reg [2:0] F; reg Cin; wire [3:0] Y; wire Zero, CarryOut, Overflow; alu_4bit uut ( .A(A), .B(B), .F(F), .Cin(Cin), .Y(Y), .Zero(Zero), .CarryOut(CarryOut), .Overflow(Overflow) ); integer i; initial begin A 4b1010; B 4b1100; Cin 0; // 遍历所有功能码 for (i 0; i 8; i i 1) begin F i; #10; $display(F%03b, A%04b, B%04b, Y%04b, Z%b, C%b, O%b, F, A, B, Y, Zero, CarryOut, Overflow); end // 算术边界测试 A 4b0101; B 4b0011; F 3b100; #10; // 5 3 A 4b0011; B 4b0101; F 3b101; #10; // 3 - 5期望Y1110 A 4b0101; B 4b0100; F 3b100; #10; // 5 4溢出 A 4b0000; B 4b0000; F 3b000; #10; // 全零Zero1 $finish; end endmodule第一段用for循环遍历全部功能码确保每个功能在固定输入下都有预期输出这是覆盖面最基础的要求。第二段补几条边界用例专门验证进位、借位、溢出和Zero标志。别小看这几条用例它们几乎覆盖了ALU设计里所有容易出错的逻辑分支。4.3 仿真结果怎么判读在ModelSim或Vivado里跑完后$display会打印一串结果。下面是我实际跑出来的几行关键输出F100, A0101, B0011, Y1000, Z0, C0, O0 F101, A0011, B0101, Y1110, Z0, C0, O0 F100, A0101, B0100, Y1001, Z0, C0, O1第一行是53结果8十六进制显示为1000无进位无溢出完全正确。第二行是3减5结果应该是-2用四位补码表示就是1110C0表示有借位O0说明3-5的结果没超过-8到7的范围这些都是对的。重点看第三行54结果9Y显示1001从无符号角度看没问题但O1说明在有符号数解释下结果溢出因为9超过了7。这就是溢出标志存在的真正意义——它提醒你对于有符号数这个结果已经不正确了。如果开波形图看建议把Y、Zero、CarryOut、Overflow都加进wave窗口切换功能码时观察信号跳变是否和预期一致。我习惯先通过$display确认功能正确再用波形核对时序细节两条路一起走效率最高。5. 实验中最常踩的坑排查与避坑清单5.1 进位标志莫名其妙全是0这是我没统计过概率但绝对排第一的问题很多同学把CarryOut实现成了tmp[4]但tmp只声明了4位结果进位被截断仿真里永远看到C0。解决办法很简单给临时变量多留一位用5位去装载4位加法结果。减法时如果发现借位标志和预期相反先查自己采用的约定是无借位C1还是无借位C0。这个细节看着小一旦错了会让整个实验看起来很不可靠。5.2 溢出和进位混为一谈有同学直接把加法模块的CarryOut当Overflow输出对无符号数来说这确实是一样的但题目一旦要求有符号数判断这个做法就错了。回到前面那个例子549无符号时最高位没有产生进位C0但因为有符号数范围上限是7结果已经溢出O1。如果你用C去代替O溢出情况就完全漏掉了。实验报告里如果能把这个区别讲清楚老师会认为你真的理解了ALU。5.3 仿真波形出现X态组合逻辑出现X十有八九是没给默认值或者case没覆盖全。我在完整代码里提前给tmp、Y和所有标志位赋了默认值这样即使case漏掉一两个分支仿真结果也不会是X状态。如果你自己写代码时把默认值省了一旦某个分支漏写输出工具不会报错而是默默综合出一个latch。排查方法是打开综合报告搜“latch”关键字或者看波形里那些高阻态的毛刺。5.4 从四位扩展到更宽与上板验证这个模块扩展成N位其实很容易把A、B、Y都改成[WIDTH-1:0]tmp改成[WIDTH:0]Overflow判断仍然沿用“最高位进位异或次高位进位”的思路。上FPGA板时我建议把A、B、F接到拨码开关Y接4个LEDZero、CarryOut、Overflow接另外3个LED。这样你可以手动拨动输入在实体硬件上实时观察ALU行为做实验室展示项目效果很好。我在Nexys系列板卡上验证过综合完占用的资源非常少整个过程十分钟就能跑通。我自己的实测体会是这个实验只要在写代码前先把功能码表和标志位约定写在草稿纸上后面基本不用返工。反而是在调试减法借位上花了最多时间因为“无借位时C1”这个约定反直觉容易记混。给你一个我常用的口诀加法看进位减法看借位进位借位看约定溢出判断要看符号位和次高位是否同时进位。只要能把你设计的减法、溢出这两个点真正跑通并且能在别人问“为什么的时候”解释清楚底层原理这个四位ALU实验就算真正过关了。本文还有配套的精品资源点击获取
返回列表