新闻详情

新闻详情

首页 / 资讯中心 / 详情

杭电计组实验3:多功能ALU控制码、标志位与Logisim/Verilog实现

发布时间:2026/9/17 12:53:56来源:尧图网络
杭电计组实验3:多功能ALU控制码、标志位与Logisim/Verilog实现
简介杭州电子科技大学计算机组成原理与系统结构课程设计的实验三「多功能ALU设计」实验报告面向计算机、电子信息类专业学生及Verilog HDL入门者用于运算器建模与仿真验证。压缩包内1个doc文件约89KB为完整实验报告可直接参照其排版与组织。报告基于Xilinx ISE展开涵盖工程创建、Verilog模块编写、综合检查、Test Fixture激励与波形仿真的流程并给出ALU核心源码与激励代码覆盖与、或、异或、加减、比较、移位等运算及ZF、OF标志位生成逻辑。文末思考题分析该ALU无法支撑MIPS核心指令集的原因讨论有符号与无符号「AB」比较歧义及SIGctr控制位改进思路并附排错记录与RTL视图说明。已有2286人学习下载适合需要撰写同类实验报告或对照仿真代码的读者。1. 计组实验3的多功能ALU不是只多一个加法器计组实验做到第三个很多人第一次发现8 位加法器能跑通但把减法、与或非、移位、比较塞进同一个模块后波形开始变得不听话。杭电计组实验3多功能ALU设计实验要解决的就是这件事——用一组控制码选中不同运算并让进位 CF、溢出 OF、零标志 ZF、符号标志 SF 在正确的时机翻转。它上承数据通路下接控制器是 408 计组里运算器章节的硬件落地。适合正在做 Logisim 或 FPGA 实验的人要能把功能表写成控制码把真值表连成多路选择器还要在边界数据上验证标志位。做完这一版后面搭基本模型机时ALU 不再是一个黑盒。2. 多功能ALU的控制码与功能表从需求到位宽选型2.1 先定功能集合8 位 ALU 的 12 条常用指令多功能 ALU 的“多功能”不是越多越好而是把控制器真正会用到的运算收进来。常见做法是算术类保留加、减、加一、减一逻辑类保留与、或、异或、非移位类保留逻辑左移、逻辑右移、算术右移比较类保留有符号小于、相等、不等。这样 12 条左右刚好能用 4 位控制码覆盖。408 计组里常考的 ALU 功能也集中在这一组尤其是减法用补码实现、比较用减法结果判断这两条决定了后面的接线方式。先把功能集合写成表再谈电路。表里要同时写清楚操作码、运算表达式和受影响的标志位否则连 Logisim 时容易漏掉某个输出。标志位不要每个运算都置位通常只有加减法影响 CF 和 OF逻辑运算只影响 ZF 和 SF。把影响范围写进表里仿真时才知道该看哪个信号。2.2 控制码编码与位宽参数让同一套逻辑适配 8/16 位控制码一般用 4 位op[3:0]从0000开始编号。编号顺序会影响多路选择器的连线难度把加法放在0000、减法放在0001可以让最低位直接参与“加/减”选择把逻辑运算放在中间移位放在高位比较放在末尾选择器的层级会清楚一些。位宽不要写死。实验箱常见 8 位但基本模型机可能扩到 16 位写成参数WIDTH以后加法器、移位器和比较器都能复用。下面是一段 Verilog 的控制码定义放在alu.v的头部后面用case展开。用localparam而不是宏综合工具更容易做常量传播也不会污染全局。// 控制码定义按功能分组便于多路选择器分层 localparam OP_ADD 4b0000; // 加法 localparam OP_SUB 4b0001; // 减法 localparam OP_INC 4b0010; // 加一 localparam OP_DEC 4b0011; // 减一 localparam OP_AND 4b0100; // 按位与 localparam OP_OR 4b0101; // 按位或 localparam OP_XOR 4b0110; // 按位异或 localparam OP_NOT 4b0111; // 按位取反 localparam OP_SLL 4b1000; // 逻辑左移 localparam OP_SRL 4b1001; // 逻辑右移 localparam OP_SRA 4b1010; // 算术右移 localparam OP_SLT 4b1011; // 有符号小于 localparam OP_EQ 4b1100; // 相等 localparam OP_NEQ 4b1101; // 不等参数说明OP_ADD到OP_DEC归入算术组减法用a (~b) 1或a - b实现OP_SLL到OP_SRA归入移位组算术右移要保留符号位OP_SLT到OP_NEQ归入比较组比较结果只占 1 位其余位补零。控制码一旦定下功能表就不能再随意改否则控制器那边也要跟着改。我一般会把这张表贴在实验报告第一页连线时逐行打勾。2.3 参数表与真值表从表格推导多路选择器把功能集合、控制码和标志位放在同一张表里是后面画电路和写case的依据。下面这张表按 8 位、WIDTH8给出。Y列写结果表达式CF/OF只对加减法有意义ZF/SF对所有运算都有效。加减法的CF表示无符号进位或借位OF表示有符号溢出这两个标志在实验里最容易混。op[3:0]助记符运算Y 输出CFOFZF/SF0000ADDa bsum[7:0]sum[8]同号相加得异号有效0001SUBa - bdiff[7:0]diff[8]异号相减得异号有效0010INCa 1sum[7:0]sum[8]同 ADD有效0011DECa - 1diff[7:0]diff[8]同 SUB有效0100ANDa b按位与00有效0101ORa | b按位或00有效0110XORa ^ b按位异或00有效0111NOT~a按位取反00有效1000SLLa 1左移一位移出位0有效1001SRLa 1逻辑右移移出位0有效1010SRA$signed(a) 1算术右移00有效1011SLT$signed(a) $signed(b)1 或 000有效1100EQa b1 或 000有效1101NEQa ! b1 或 000有效表格里sum和diff要扩一位。加法用{1b0, a} {1b0, b}减法用{1b0, a} - {1b0, b}这样最高位就是进位或借位。无符号比较可以直接看diff[8]a b时借位为 1有符号比较不能只看借位要比较符号和差值。逻辑运算和移位运算把CF/OF固定为 0不是偷懒而是让控制器只在高位控制码下查询这两个标志减少无效跳转。从真值表到电路通常用两级多路选择器第一级选算术结果加、减、加一、减一第二级选逻辑、移位和比较结果。控制码的高两位可以拿来分组低两位用来选组内具体项。这样比直接用一个 14 选 1 的选择器更容易查线也好在 Logisim 里分屏显示。移位器可以先用一个桶形移位器或者按实验要求只做一位移位后者连线更少但功能表要写清楚只支持移一位。3. 杭电计组实验3的 Logisim 搭法数据通路与标志位接线3.1 子电路引脚划分与元件库选型Logisim 里先建一个子电路命名ALU8。输入引脚放A[7:0]、B[7:0]、op[3:0]输出引脚放Y[7:0]、ZF、CF、OF、SF。如果实验要求带使能端再加en但多数版本不加直接靠控制码保持输出。元件库主要用Wiring里的分线器、Arithmetic里的加法器和比较器、Memory里的多路选择器以及Gates里的与或非异或门。不要一开始就用Arithmetic里的 ALU 元件那个封装太厚实验要的是自己搭出数据通路。分线器要先按位宽设置。A和B各用一个 8 位分线器拆成高低位后面取符号位和移位时要用。多路选择器选Multiplexer数据位宽设为 8选择端位数按分组数算。如果做两级选择第一级选 4 个算术结果用 2 位选择端第二级选 4 组结果再用 2 位选择端。控制码op[3:2]接第二级op[1:0]接第一级接线时别把高低位接反。元件库位置用途关键参数分线器Wiring拆分 A/B 的位位宽 8扇出按需加法器Arithmetic加、减、加一、减一位宽 8带进位输入比较器Arithmetic有符号/无符号比较位宽 8输出 1 位多路选择器Memory选择运算结果数据位 8选择端 2 位移位器Arithmetic左移、右移位宽 8方向控制与或非异或门Gates逻辑运算位宽 8按位连接3.2 加减法共用加法器的连线步骤减法不要单独再放一个减法器用加法器加补码更贴近 408 计组里的运算器结构。步骤是把B的每一位经过一个异或门异或门的另一输入接sub控制信号sub1时B取反同时加法器的进位输入Cin接sub完成加一。这样sub0时算ABsub1时算A~B1也就是A-B。加法器输出扩到 9 位第 8 位就是进位或借位。溢出判断不要直接接加法器的进位输出。无符号进位CF看第 8 位有符号溢出OF要看符号位加法时A[7]和B[7]相同且Y[7]与它们不同减法时A[7]和B[7]不同且Y[7]与A[7]不同。用异或门和与门搭出来或者用两个异或门加一个与门。接线时把sub也纳入B[7]的判断否则减法溢出会误判。加一和减一可以复用同一加法器B固定为00000001sub0就是加一sub1就是减一。也可以单独拉一个 1 位常量减少选择器输入。实验报告里最好画清楚这个复用关系答辩时老师常问“减法怎么实现的”。我一般会把sub信号同时接到B的异或门和Cin在图上用同一根线标出来。3.3 移位、比较与标志位生成移位器用Arithmetic里的Shifter设置方向为左或右移位位数设为 1。逻辑左移补 0逻辑右移补 0算术右移补符号位。Logisim 的 Shifter 如果只有方向控制算术右移要用A[7]复制 8 位后与右移结果做选择A[7]0时逻辑右移和算术右移一样A[7]1时高两位补 1。比较器直接输出 1 位小于、相等、不等三个结果分别接多路选择器。比较器的有符号模式要确认设置否则SLT会变成无符号比较。标志位生成放在最后一级ZF (Y 0)用 8 输入或非门实现SF Y[7]直接取最高位CF和OF从加减法结果里取逻辑和移位运算时用控制码屏蔽。屏蔽可以用与门op属于算术组时CF有效否则强制 0。控制码算术组是0000到0011用两位高位和两位低位组合出一个is_arith信号接与门。# 生成 Logisim ROM 可加载的测试向量每行 3 字节 A,B,op另存期望 Y # 用法python gen_alu_vectors.py alu_vectors.hex for a in [0x00, 0x01, 0x7F, 0x80, 0xFF, 0x55, 0xAA]: for b in [0x00, 0x01, 0x7F, 0x80, 0xFF, 0x55, 0xAA]: for op in range(0x10): y (a b) 0xFF if op 0x0 else (a - b) 0xFF if op 0x1 else a b print(f{a:02X}{b:02X}{op:X}{y:02X})这段脚本只列了一部分运算用来看关键边界0x7F 0x01会溢出0x80是符号位为 1 的最小负数0xFF是全 1。把输出保存成十六进制文件在 Logisim 里用ROM加载再逐行对照Y和标志位。参数说明a和b是测试输入op是 4 位控制码y是低 8 位结果。跑之前先确认脚本里的运算和功能表一致否则会把错误期望带进仿真。3.4 用 Python 生成测试向量灌入 Logisim ROMLogisim 的ROM地址输入接计数器数据输出分成A、B、op、Y_expected四段。计数器时钟用Clock元件频率设低一点比如 1 Hz方便看单步。每来一个时钟ROM 输出一组向量同时接到ALU8的输入和比较器的一端比较器另一端接Y_expected输出match。如果match一直为 1说明当前测试向量通过如果变 0记下地址回到脚本查那一行。这一步常见坑是分线器位宽和 ROM 数据位宽不一致。A、B、Y_expected各 8 位op4 位一共 28 位ROM 数据位宽要设 28地址位宽按测试向量条数算。地址线不要接错到op段否则会看到结果时对时错。也可以在 Logisim 里手动改A/B/op先跑几条A7F, B01, op0000期望Y80, CF0, OF1, ZF0, SF1。这条过了再做A80, B01, op0001期望Y7F, CF1, OF1, ZF0, SF0。手动过两条边界比盲跑一堆向量更容易定位接线错误。4. 用 Verilog 写一版可综合的多功能 ALU代码与仿真4.1 参数化 ALU 的 Verilog 实现Verilog 版本适合往 FPGA 实验板上搬也方便用iverilog做自动化仿真。模块端口保持和 Logisim 子电路一致位宽用parameter WIDTH。所有输出用reg在always (*)里用case赋值综合工具会展开成多路选择器。加减法先扩一位统一算好sum和diff避免在case里重复写位宽转换。module alu #(parameter WIDTH 8)( input wire [WIDTH-1:0] a, input wire [WIDTH-1:0] b, input wire [3:0] op, output reg [WIDTH-1:0] y, output reg zf, output reg cf, output reg of, output reg sf ); // 扩展一位最高位作为进位/借位 wire [WIDTH:0] sum {1b0, a} {1b0, b}; wire [WIDTH:0] diff {1b0, a} - {1b0, b}; wire [WIDTH:0] inc {1b0, a} 1b1; wire [WIDTH:0] dec {1b0, a} - 1b1; always (*) begin y {WIDTH{1b0}}; cf 1b0; of 1b0; case (op) 4b0000: begin y sum[WIDTH-1:0]; cf sum[WIDTH]; of (a[WIDTH-1] b[WIDTH-1]) (y[WIDTH-1] ! a[WIDTH-1]); end 4b0001: begin y diff[WIDTH-1:0]; cf diff[WIDTH]; // 借位 of (a[WIDTH-1] ! b[WIDTH-1]) (y[WIDTH-1] ! a[WIDTH-1]); end 4b0010: begin y inc[WIDTH-1:0]; cf inc[WIDTH]; of (a[WIDTH-1] 1b0) (y[WIDTH-1] 1b1); end 4b0011: begin y dec[WIDTH-1:0]; cf dec[WIDTH]; of (a[WIDTH-1] 1b1) (y[WIDTH-1] 1b0); end 4b0100: y a b; 4b0101: y a | b; 4b0110: y a ^ b; 4b0111: y ~a; 4b1000: y {a[WIDTH-2:0], 1b0}; // 逻辑左移 4b1001: y {1b0, a[WIDTH-1:1]}; // 逻辑右移 4b1010: y {a[WIDTH-1], a[WIDTH-1:1]}; // 算术右移 4b1011: y ($signed(a) $signed(b)) ? {{(WIDTH-1){1b0}}, 1b1} : {WIDTH{1b0}}; 4b1100: y (a b) ? {{(WIDTH-1){1b0}}, 1b1} : {WIDTH{1b0}}; 4b1101: y (a ! b) ? {{(WIDTH-1){1b0}}, 1b1} : {WIDTH{1b0}}; default: y {WIDTH{1b0}}; endcase zf (y {WIDTH{1b0}}); sf y[WIDTH-1]; end endmodule逻辑说明sum、diff、inc、dec都扩到WIDTH1位case里只取低WIDTH位赋给y最高位给cf。加减法的of用符号位判断加一和减一单独判断符号翻转。逻辑和移位运算把cf/of保持为 0比较结果只放最低位其余位补 0。zf和sf在所有分支之后统一计算避免每个分支重复写。参数WIDTH改成 16 时移位表达式仍然成立比较器的位宽也跟着变。信号方向位宽说明a输入WIDTH操作数 Ab输入WIDTH操作数 Bop输入4控制码y输出WIDTH运算结果zf输出1结果为零cf输出1无符号进位/借位of输出1有符号溢出sf输出1结果符号位注意$signed(a) $signed(b)依赖综合工具支持有符号比较如果工具报错可以改写成(a[WIDTH-1] ^ b[WIDTH-1]) ? a[WIDTH-1] : (a b)。算术右移在WIDTH1时会有位选越界实验里位宽至少 8不用考虑。4.2 自检 testbench覆盖进位、溢出与零标志测试平台不要只给几个随机数要把边界固定下来。下面这段 testbench 用任务封装每调一次打印A、B、op、Y和四个标志方便和功能表对照。重点覆盖0x7F0x01、0x800x01、0x00-0x01、0xFF0x01、0x801、0x7F0x80这几组。timescale 1ns/1ps module alu_tb; reg [7:0] a, b; reg [3:0] op; wire [7:0] y; wire zf, cf, of, sf; alu #(.WIDTH(8)) dut(.a(a), .b(b), .op(op), .y(y), .zf(zf), .cf(cf), .of(of), .sf(sf)); task check; input [7:0] ta, tb; input [3:0] top; begin a ta; b tb; op top; #10; $display(A%02X B%02X op%X Y%02X ZF%b CF%b OF%b SF%b, a, b, op, y, zf, cf, of, sf); end endtask initial begin check(8h7F, 8h01, 4b0000); // 1271有符号溢出 check(8h80, 8h01, 4b0000); // -1281不溢出 check(8h00, 8h01, 4b0001); // 0-1借位 check(8hFF, 8h01, 4b0000); // 2551无符号进位 check(8h80, 8h00, 4b1010); // 算术右移保留符号 check(8h7F, 8h80, 4b1011); // 127 -128有符号比较 check(8h55, 8hAA, 4b0110); // 异或 check(8h00, 8h00, 4b1100); // 相等 $finish; end endmodule任务check的三个参数是ta、tb、top分别对应被测 ALU 的输入。#10是等待组合逻辑稳定$display的格式里%02X输出两位十六进制%b输出二进制标志。每一行后面注释了期望现象仿真时逐行核对。如果OF在0x800x01变成 1说明溢出判断写成了“结果最高位为 1”那是错的0x80是负数加正数结果0x81仍是负数不溢出。4.3 用 iverilog 跑仿真并看波形命令行仿真用iverilog加vvp如果装了波形工具再加gtkwave。下面三条命令分别编译、运行、看波形。-o指定输出文件-Wall打开警告能提前发现位宽截断和未连接信号。$dumpfile和$dumpvars要加在 testbench 的initial里否则没有波形文件。# 编译 ALU 和 testbench打开警告 iverilog -Wall -o alu_sim alu.v alu_tb.v # 运行仿真终端打印 $display 结果 vvp alu_sim # 如果 testbench 里写了 $dumpfile(alu.vcd)用 gtkwave 打开 gtkwave alu.vcd 参数说明-Wall会提示WIDTH相关的隐式位宽转换-o alu_sim生成的可执行文件由vvp解释gtkwave后面的是放到后台不占用终端。如果vvp报error: Unable to open input file先确认alu.v和alu_tb.v在同一目录模块名和文件名一致。波形里重点看y、cf、of把时间轴缩放到每次check调用的位置和终端打印对照。Verilog 仿真和 Logisim 手动连线会得出同一组结果前提是控制码表一致。常见不一致是 Logisim 里移位器的方向位接反或者 Verilog 里用了但没加$signed。排错时先把A、B、op固定成一条边界向量两边同时跑看Y和CF/OF哪一位先分叉。分叉点通常就是接线或表达式写错的位置。5. ALU 边界用例与排错进位、溢出、零标志最容易翻车的地方5.1 边界测试表与自动化穷举边界数据比随机数据更能暴露 ALU 的问题。下面这张表列出 8 位 ALU 最该先跑的用例每一行都能对应到功能表里的标志位规则。手动跑完这些再做穷举或随机测试效率更高。用例ABop期望 Y期望 CF期望 OF期望 ZF期望 SF最大正数加一7F01ADD800101最小负数加一8001ADD810001全 1 加一FF01ADD001010零减一0001SUBFF1001负数减正数8001SUB7F1100算术右移负数8000SRAC00001有符号比较7F80SLT010000相等比较5555EQ010000穷举测试可以写一个 Python 脚本针对 8 位输入生成所有A、B和主要控制码把期望值写成 Verilog$readmemh能读的格式。也可以直接在 testbench 里用双重for循环把a和b从 0 到 255 遍历。穷举 8 位加法的全部组合是 65536 组仿真时间可以接受如果位宽是 16 位就改成随机采样加边界组合否则组合数会涨到 40 亿以上。5.2 常见错误与定位方法第一个高频错误是把无符号进位和有符号溢出混为一谈。FF01的CF1但OF0因为按有符号数-110没有溢出7F01的CF0但OF1因为1271超出 8 位有符号范围。定位方法是在波形里同时看a[7]、b[7]、y[7]加法溢出要求a[7]和b[7]相同且y[7]与它们不同。第二个错误是减法借位判断反了。a - b在a b时diff[WIDTH]为 1表示借位CF应该置 1如果接成~diff[WIDTH]00-01就会得到CF0。减法溢出要求a[7]和b[7]不同且y[7]与a[7]不同别直接套加法的条件。第三个错误是ZF生成时机不对。ZF应该在多路选择器输出之后计算而不是在某个运算分支里提前算。把所有分支写完后统一zf (y 0)就不会出现“逻辑运算零标志正常、移位运算零标志丢失”的情况。SF也一样统一取y的最高位。第四个错误是控制码冲突。两个不同的case分支用了同一个op值综合工具可能只保留一个仿真和 Logisim 表现不一致。检查方法很简单把功能表里的op列排序看有没有重复写完case后在default分支给y赋 0避免锁存器。最后用iverilog -Wall重新编译一遍警告数量降到 0 再上板。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

新工科Java课程改革:从语法到工程实践的能力重塑 2026/9/17 14:42:36

新工科Java课程改革:从语法到工程实践的能力重塑

简介:这是一份新工科背景下Java程序设计课程教学改革的参考文献,面向高校计算机专业教师、教学管理者及课程改革研究者,尤其适合准备进行课程大纲调整的团队。内容聚焦Java课程教学中理论与实操脱节、缺少项目实践、评价机制单一、学生自主学…

阅读更多 →
Win11开机提速16秒:5项安全可逆的系统级调优 2026/9/17 14:42:36

Win11开机提速16秒:5项安全可逆的系统级调优

1. 项目概述:一次被低估的系统性能博弈“Win11 比 Win10 慢 16 秒?同一台电脑实测,5 个设置改完反超”——这个标题不是营销噱头,而是我在自己那台服役四年的戴尔XPS 13 9310上亲手掐表、反复验证的真实结果。它背后藏着一个被多数…

阅读更多 →
在 Xinference 中部署 GPT-2:内置模型注册、引擎选择与 launch 命令实战指南 2026/9/17 14:42:36

在 Xinference 中部署 GPT-2:内置模型注册、引擎选择与 launch 命令实战指南

在 Xinference 中部署 GPT-2:内置模型注册、引擎选择与 launch 命令实战指南 【免费下载链接】inference Swap GPT for any LLM by changing a single line of code. Xinference lets you run open-source, speech, and multimodal models on cloud, on-prem, or yo…

阅读更多 →
Riot.js 中集成 tsParticles 粒子动画:riot-particles-demo 的启动、测试与构建实战指南 2026/9/17 14:42:35

Riot.js 中集成 tsParticles 粒子动画:riot-particles-demo 的启动、测试与构建实战指南

Riot.js 中集成 tsParticles 粒子动画:riot-particles-demo 的启动、测试与构建实战指南 【免费下载链接】tsparticles tsParticles - Easily create highly customizable JavaScript particles effects, confetti explosions and fireworks animations and use the…

阅读更多 →
深入解析 Lingo.dev Compiler 转换管道:React 组件构建期自动化翻译注入的完整实现 2026/9/17 14:42:35

深入解析 Lingo.dev Compiler 转换管道:React 组件构建期自动化翻译注入的完整实现

深入解析 Lingo.dev Compiler 转换管道:React 组件构建期自动化翻译注入的完整实现 【免费下载链接】replexica Open-source localization engineering tools. Connects to Lingo.dev localization engineering platform for consistent, quality translations. 项…

阅读更多 →
使用 GitHub Copilot SDK 在 .NET 中构建 Copilot Agent 扩展:包引入、六大护栏与会话生命周期实战 2026/9/17 14:39:32

使用 GitHub Copilot SDK 在 .NET 中构建 Copilot Agent 扩展:包引入、六大护栏与会话生命周期实战

使用 GitHub Copilot SDK 在 .NET 中构建 Copilot Agent 扩展:包引入、六大护栏与会话生命周期实战 【免费下载链接】skills Repository for skills to assist AI coding agents with .NET and C# 项目地址: https://gitcode.com/GitHub_Trending/skills17/skills…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞