CPU设计与体系结构实验手册:从加法器到五级流水线的FPGA实战指南

发布时间:2026/10/16 22:29:22
CPU设计与体系结构实验手册:从加法器到五级流水线的FPGA实战指南
简介CPU设计与体系结构实验指导手册教师版面向高校计算机专业师生依托龙芯LS-CPU-EXB系列教学实验平台适用于《数字逻辑》《计算机组成原理》《计算机体系结构》等课程的实验教学。全书从基础的数据运算起步逐步覆盖定点加法器、定点乘法器、寄存器堆、ALU模块、存储器实现并深入到单周期CPU与多周期CPU的设计课程设计部分包含静态5级流水线CPU实现与优化拓展附录还提供MIPS指令集及CP0寄存器说明便于按教学进度分阶段选用。资源包内为1个PDF文档共6.09MB目录层级清晰实验目的、设备、任务与设计流程均有完整呈现。已有680人学习对需要系统开展CPU实验的教师和学生具有较高参考价值也可作为自学FPGA数字系统设计与处理器体系结构的配套教材。1. 为什么一份 CPU 设计与体系结构实验手册值得照做做 CPU 设计与体系结构实验最怕的不是难而是不知道每一步该做什么、做完怎么算对。很多人拿到实验箱第一步不是写代码而是花大量时间在工程创建、引脚约束、触摸屏调用这些外围事情上真正留给 CPU 设计本身的时间反而没多少。这份实验指导手册的价值正在于它把从定点加法到五级流水线的完整路线一次性铺开实验一到实验七对应组成原理的验证型实验课程设计及拓展对应体系结构的综合型设计每一步都有参考代码、仿真流程和上板验证机制属于可以照着做、做完能验收的全流程方案。手册基于 Xilinx Vivado 开发环境、Artix-7 系列 FPGA 实验板设计和实现用 Verilog 完成全部 CPU 实验。适合几类人正在做计算机组成原理或体系结构课设的本科生、需要指导实验的助教和教师以及想在自己板子上复现一套 MIPS 教学 CPU 的爱好者。下面按我的使用经验讲清楚这套实验系统怎么跑通、哪些模块可以直接抄、哪些地方最容易翻车。2. 实验平台与工程搭建先从实验箱和 Vivado 环境讲起2.1 实验箱的资源构成与接口约定LS-CPU-EXB-002 实验箱的核心是一块 FPGA 实验板配套 Xilinx 下载线、串口线含 USB 转串口和电源适配器。和普通开发板最大的区别是它实现了 LCD 触摸屏的硬件驱动不需要处理器核就能直接使用显示和输入功能并且提供了封装好的调用接口。这意味着上板验证时不必依赖 64 个拨码开关和 32 个 LED 来观察 32 位运算结果而是通过触摸屏输入加数、显示结果大大降低了外设设计的负担。实验箱在配套资源里把 lcd_module 做成了黑盒网表文件学生只需要按照接口调用不用关心 LCD 时序的具体实现。我一般会提醒学生先搞清楚两层结构顶层模块负责整个实验的对外交互触摸屏输入输出、时钟、复位功能模块才是实验真正要设计的对象。以定点加法为例顶层模块 adder_display 调用两个子模块一个是自己写的加法器 adder另一个是封装好的 lcd_module。这种“外围展示模块 功能模块”的分层方式后面几乎所有实验都可以沿用。2.2 Vivado 工程创建的三个关键动作新建工程时最容易踩坑的是器件型号选错。手册指定的是 Artix-7 系列、fbg676 封装下的 xc7a200tfbg676-2这个型号和实验板上的 FPGA 芯片一一对应。如果选错综合布局布线可能通过但下载到板子上必然出问题属于硬件平台的“玄学”问题其实根因就是型号不一致。添加源文件方面Verilog 代码可以提前在其他编辑器里写好再添加也可以在工程里新建。手册的参考做法是点击 Add Sources选择 Add or create design sources按文件或文件夹添加。工程管理区会显示模块间的层次关系如果 lcd_module 前面带问号说明该网表文件还没添加。这时候需要单独添加 lcd_module.dcp注意 Vivado 2012.3 版本要先解压 dcp 文件再添加我用的更新版本 Vivado 直接添加也能识别。这一步是很多新手第一次接触黑盒网表容易忘记导致综合时报找不到模块。2.3 引脚约束文件怎么读怎么改约束文件.xdc的作用是把顶层模块的端口绑定到 FPGA 板上的物理引脚。手册给出的 adder.xdc 内容看起来很长其实按功能划分就三部分时钟与复位、拨码开关与 LED、LCD 触摸屏接口。set_property PACKAGE_PIN AC19 [get_ports clk] set_property PACKAGE_PIN A3 [get_ports led_cout] set_property PACKAGE_PIN Y3 [get_ports resetn] set_property PACKAGE_PIN AC21 [get_ports input_sel] set_property PACKAGE_PIN Y6 [get_ports sw_cin] set_property IOSTANDARD LVCMOS33 [get_ports clk] set_property IOSTANDARD LVCMOS33 [get_ports resetn] # LCD 触摸屏接口 set_property PACKAGE_PIN J25 [get_ports lcd_rst] set_property PACKAGE_PIN H18 [get_ports lcd_cs] set_property PACKAGE_PIN K16 [get_ports lcd_rs]PACKAGE_PIN 决定信号接到 FPGA 的哪个物理引脚IOSTANDARD 指明电平标准。实验板上几乎全部 IO 都使用 LVCMOS33也就是 3.3V 的 LVCMOS 电平。约束文件的修改原则是自己设计的模块端口名必须和 xdc 里的 get_ports 名字完全一致否则约束不生效。一般我会把约束文件里的端口名做成顶层模块的端口名对照表检查一遍再综合能省掉很多上板后“没反应”的排查时间。3. 定点运算实验从加法器到乘法器的数据通路基础3.1 加法器参考实现与“”号背后的设计选型实验一是整个实验体系的入门核心任务是让使用者熟悉实验箱和 Vivado 的设计流程。功能模块 adder.v 是典型的基础运算模块代码如下timescale 1ns / 1ps module adder( input[31:0] operand1, input[31:0] operand2, input cin, output [31:0] result, output cout ); assign {cout,result} operand1 operand2 cin; endmodule这段代码有两点值得注意。第一直接使用“”运算符综合工具会自动调用库里的加法器 IP相比手动用全加器搭建面积和时序表现通常更优。第二输出端用拼接符把进位 cout 和 32 位结果 result 合成 33 位语法简洁也避免进位丢失。但在某些教学场景下教师会要求用全加器逐位搭建以理解进位链原理。我一般建议先按手册默认的“”实现跑通流程再在理解原理后手动改写为行波进位结构对比综合报告这比直接上难度更容易接受。3.2 testbench 怎么搭才能快速验证正确性手册强调“一个比较完备的 testbench 就是产生输入激励送入到要测试的功能模块里然后读出执行结果与预期比较”但在入门实验里只产生输入激励就足够了。testbench 的核心代码有两点值得抄timescale 1ns / 1ps module testbench; reg [31:0] operand1; reg [31:0] operand2; reg cin; wire [31:0] result; wire cout; adder uut ( .operand1(operand1), .operand2(operand2), .cin(cin), .result(result), .cout(cout) ); initial begin operand1 0; operand2 0; cin 0; #100; end always #10 operand1 $random; always #10 operand2 $random; always #10 cin {$random} % 2; endmodule随机激励设计是这段代码的精华。每 10ns 给两个操作数赋一个随机 32 位数进位输入通过拼接符 {$random} 保证非负后再取模 2得到 0 或 1。由于加法器没有时序逻辑随机测试配合波形观察覆盖几百组数据后功能基本可以认为是正确的。仿真时有两个操作习惯能显著提升效率。一个是把要观察的信号添加到波形窗口后必须点击 Relaunch 重跑才能看到新信号数据另一个是 32 位二进制数据在波形窗口里完全没法看选中信号后右键 Radix 改成十六进制一眼就能核对结果。手册里举的例子是 b897be71 42f24186 1 fb89fff7这类手工核对非常适合作为验收证据。3.3 定点乘法移位与累加的实现思路实验二从加法进阶到乘法复杂度提升的关键在于理解“移位加”的运算本质。乘法器在数字电路里有多种实现策略最简单的迭代乘法器通过移位和累加完成 n 次部分积求和面积小但需要多个周期组合逻辑乘法器并行产生所有部分积再用加法树压缩速度快但资源消耗大。在组成原理课程层面手册并没有要求实现 Booth 乘法器这类高级结构重点是让学习者理解乘法的数据通路被乘数左移、乘数右移、部分积累加、符号处理。这一步为后面积累 ALU 和 CPU 实现奠定基础因为乘除指令在 MIPS 指令集中通常使用独立的多周期部件而非组合逻辑直接完成。我第一次做这个实验时总想着把乘法做成单周期并行结构结果资源占用和时序约束都很难看后来按迭代结构实现综合报告立刻正常了。4. 核心部件实现寄存器堆、ALU 与存储器的模块化设计4.1 寄存器堆读写端口的设计约束实验三实现 MIPS 寄存器堆这是 CPU 数据通路里复用频率最高的部件。在 MIPS 架构中寄存器堆通常需要两个读端口和一个写端口这是双指令发射或者流水线结构能并行读两个操作数的硬件基础。手册实验要求理解通用寄存器、状态寄存器等不同类型寄存器的作用并在硬件中构建它们。寄存器堆的端口设计一般遵循以下约定信号名方向位宽功能说明clkinput1时钟信号上升沿写有效weinput1写使能为高时允许写入raddr1input5读端口 1 地址可读 32 个寄存器raddr2input5读端口 2 地址waddrinput5写端口地址wdatainput32写数据rdata1output32读端口 1 输出rdata2output32读端口 2 输出实现时需要注意两点寄存器堆要么用 always 块配合 case 语句实现要么直接调用 Xilinx 的分布式 RAM 原语。教学实验通常要求手写 Verilog 以理解内部结构而综合工具会把行为描述自动映射到 FPGA 的 Block RAM 或 LUTRAM 上。另外地址 0 号寄存器在 MIPS 约定中恒为 0写入无效这一点会在后续指令执行中产生微妙影响比如冒险处理时如果忽略了零寄存器恒零的特性流水线转发逻辑会出错。4.2 ALU 模块功能码与运算控制的划分实验四实现 ALU这是 CPU 的运算核心。ALU 的接口设计直接决定后续单周期 CPU 的连线方式一般包括两个 32 位操作数输入、一个 4 位或 5 位功能选择信号、一个 32 位结果输出以及零标志、进位、溢出等状态输出。功能选择信号建议用局部参数定义而非魔法数字可读性和可维护性更好。手册虽然没有给出完整 ALU 代码但实验要求涵盖算术运算加减乘除、逻辑运算与或非异或、移位运算逻辑左移右移、算术右移和比较运算小于比较、相等比较。这里最容易犯的错误是把所有运算都用行为级描述直接写在一个 always 块里导致综合出优先级编码器结构。实际上 ALU 更适合用组合逻辑加多路选择器实现把每个运算独立描述最后通过 case 选择输出这样综合后的电路结构清晰、时序路径也更短。4.3 存储器实现读时序与写时序的差异实验五实现存储器模型通常包括指令存储器和数据存储器两部分。教学实验里存储器可以直接用 Verilog 数组加 always 块构建行为模型但这与真实 SRAM 的时序行为有差别真实 SRAM 的读操作是电平触发、有地址建立时间和读延迟而行为模型如果不加时钟控制很容易做成组合逻辑读导致后续 CPU 时序错乱。手册里存储器实验的目标是理解 CPU 与存储器之间的交互关系包括大端小端、地址对齐和读写使能控制。我通常的做法是指令存储器做成同步读保证取指阶段在时钟边沿之后拿到稳定指令数据存储器做成同步写、组合读或同步读取决于 CPU 设计要求。最简单可靠的方案是让存储器的读写都同步于时钟读地址在时钟上升沿锁存输出在下一个时钟沿稳定。这样虽然多一个周期的延迟但对单周期 CPU 的时序收敛非常友好。5. 从单周期到多周期再到流水线CPU 设计路线的核心矛盾5.1 单周期 CPU所有指令一个周期完成的设计约束实验六实现单周期 CPU这是整个手册承上启下的关键。单周期 CPU 的特点是指令周期固定为一个时钟周期所有指令无论复杂程度如何都在一个周期内完成取指、译码、执行、访存和写回。这会带来两个明显问题一是时钟周期必须按最慢指令的延迟来设定导致整体性能被拖累二是数据通路里所有部件只需要一套控制逻辑按指令类型输出不同控制信号设计相对简单。单周期 CPU 的指令集范围见附录 A1通常包含 MIPS 的 lw、sw、beq、add、sub、and、or、slt 等基础指令。实现时要把指令译码产生的控制信号逐条列成真值表我记得当时就是对着指令真值表逐个核对控制信号才算真正理解了每条指令在数据通路上的流动过程。一个典型的翻车点是立即数扩展的处理。MIPS 的 beq 分支指令需要把 16 位立即数左移两位再符号扩展而 lw/sw 只需要符号扩展到 32 位。如果复用同一个扩展模块而不区分操作类型访存指令和分支指令的地址计算就会出错而且这种错误仿真时偶尔能通过某些数据上板后行为不稳定属于比较隐蔽的 bug。5.2 多周期 CPU控制信号的分步拆解实验七从单周期演进到多周期目的在于解决单周期时钟频率受最慢指令拖累的问题。多周期 CPU 把每条指令的执行拆分成多个步骤每个时钟周期只完成一个微操作不同指令所需的周期数不同控制逻辑从组合逻辑转变为有限状态机。以 lw 指令为例多周期执行通常分为五步取指、译码并读寄存器、计算地址、读数据存储器、写回寄存器。每一步处于不同的状态状态机的状态转换由指令类型决定。这意味着控制单元不再是一张组合逻辑真值表而是一组状态转移逻辑。手册附录 A2 列出了多周期 CPU 相对单周期新增的指令主要包括 jal、jr 等跳转类指令和乘除相关的特殊指令。多周期设计的难点在于状态机的编写方式。如果使用一段式状态机把所有状态转换和输出逻辑写在一个 always 块里代码虽然短但可读性极差后续调试很容易迷失。建议使用三段式第一段描述状态寄存器的次态输出第二段用组合逻辑描述状态转移条件第三段描述各状态下的控制信号输出。这样控制信号和状态转移各自独立出问题时能快速定位是哪一段逻辑计算错误。5.3 流水线 CPU 与冒险处理课程设计的验收重点课程设计要求实现静态 5 级流水线 CPU这是从“理解 CPU 工作原理”跨入“优化 CPU 系统”的分水岭。流水线把指令执行分为取指、译码、执行、访存、写回五个阶段理论上吞吐率提升接近 5 倍但实际上结构冒险、数据冒险和控制冒险会显著拉低实际性能。手册提供的思路是静态流水线也就是通过编译器或者程序员来避免部分冒险硬件上只做必要的停顿和转发。数据冒险的典型处理是前向通路在 EX 阶段计算结果出来后直接把结果旁路到后续指令的 ALU 输入而不是等待写回寄存器堆。控制冒险则需要分支预测或流水线冲刷静态预测通常采用“不跳转”策略预测错误时清除已进入流水线的错误指令。在附录 A3 中可以看到流水线 CPU 新增的指令范围比如支持更多逻辑运算和数据处理指令。CP0 寄存器组在附录 B 中单独列出用于实现异常处理和处理器状态控制。我做过一次课程设计的答辩被问到最多的问题就是“流水线冒险怎么处理的”因此建议在提交方案时把三张图准备好流水线数据通路图、前向通路连接图、控制冒险处理流程图。这三张图一摆验收老师基本就能确认你是真的理解了流水线机制而不是只改了别人的代码。6. 常见问题排查五个最容易翻车的细节及对策6.1 现象仿真波形乱七八糟随机数据对不上原因testbench 里多个 always 块同时驱动同一个信号或者 initial 块里的初始化赋值和 always 块的连续赋值冲突。解决检查 testbench 中每个 reg 类型变量是否只有一个驱动源。如果既在 initial 里赋值又在 always 里赋值仿真相当于多驱动器同时作用数据竞争不可避免。统一改成 only always 随机激励或只在 initial 里赋值一次后靠 always 产生波形。6.2 现象工程综合时报找不到 lcd_module 模块原因dcp 网表文件没有正确添加或者添加的网表版本与 Vivado 版本不匹配。解决在工程管理区确认 lcd_module 的状态图标不是问号。如果是问号重新 Add Sources 添加 .dcp 文件。老版本 Vivado 把 dcp 当成普通文件添加后还需要在 Sources 窗口里右键设置属性为 Design Sources。通常的排查顺序是先看文件有没有加进来再看文件类型是否正确。6.3 现象上板后触摸屏无显示或不响应原因引脚约束没生效或者 LCD 相关的 IOSTANDARD 缺失。解决检查顶层模块的端口名和 xdc 文件里的 get_ports 是否一字不差注意大小写和数组声明方式。LCD 数据线是 16 位总线约束文件里必须写成 {lcd_data_io[0]} 这种展开形式不能只约束其中一个。其次检查 IOSTANDARD 是否全部设置为 LVCMOS33漏掉任何一个都会导致 IO 电平不匹配。6.4 现象仿真时信号对不上但逻辑看起来没问题原因波形窗口显示的是二进制32 位数据无法直观核对。解决这是我写过无数次要提醒队友的“血泪经验”。仿真波形默认用二进制显示32 位的数据一眼根本分不清对错。把要观察的信号全部改成十六进制显示加法、存储器的地址和数据马上就能人工验算。另外新添加的信号必须 Relaunch 重启仿真才能出现波形。6.5 现象多周期 CPU 状态机跑飞指令执行到一半卡住原因状态机的默认状态没有覆盖非法状态或者状态转换条件里用了阻塞赋值和非阻塞赋值混用。解决检查 always 块里是否统一使用非阻塞赋值 。状态机里的次态逻辑用组合逻辑块阻塞赋值也没有问题但要保证状态寄存器的更新用非阻塞赋值。建议给状态机增加一个复位状态 IDLE任何非法状态都跳回 IDLE避免状态机锁死在未定义状态。调试时在波形里把 state 信号加进来单步跑几条指令看状态是否按预期切换。7. 上板验证与答辩验收从功能正确到交付一份完整方案7.1 验收流程的四个层次一套实验从写完到验收通过我建议按以下四个层次检查。第一层是功能仿真确保功能模块自己的逻辑正确第二层是集成仿真把外围展示模块和功能模块一起仿真确认触摸屏输入和显示逻辑衔接一致第三层是上板验证下载到 FPGA 后通过实际输入输出确认板上行为第四层是异常场景测试比如加法器的进位链在最大值边界0xffffffff 1时是否产生正确的进位输出。上板验证时有个细节容易被忽略下载完成后要手动按复位键让系统进入初始状态。很多实验箱的 FPGA 配置完成后不会自动复位如果不按复位触摸屏可能处于未知状态导致“屏幕亮但没有反应”。这类问题看起来像是硬件坏了其实只是初始化顺序不对。7.2 答辩汇报的素材组织技巧答辩或实验检查时不要上来就展示代码而应按照“设计思路 → 结构框图 → 仿真结果 → 上板演示”的顺序组织。我通常会准备一组对照材料设计框图一张、模块层次图一张、仿真波形图两到三张覆盖正常数据和边界数据、上板结果照片一张。实验报告里要把这几项落实为图片加文字说明其中仿真波形图必须标注关键数据的十六进制值让老师一眼确认结果正确。另外一个讲不清楚的问题就是“生成的 CPU 性能怎么样”。如果被问到流水线的加速比不要只答理论值 5 倍。实际上由于冒险停顿、分支冲刷和 load-use 停顿静态流水线的实际加速比通常在 1.5 到 2.5 之间。可以准备一个简单的指令序列统计汇编代码里相邻指令的依赖关系越多停顿越多CPI 越接近 2。如果答辩时能说出“我这个流水线在特定测试程序上 CPI 约为某个具体值”比空泛地讲流水线原理更能证明你真的做了系统实现。7.3 从这份手册里能拿走什么完整做完这套实验你手上的东西包括一个经过仿真验证的定点加法模块、一个乘法模块、一个双读单写端口寄存器堆、一个支持基本 MIPS 指令的 ALU、一个同步存储器模型以及单周期、多周期、静态五级流水线三套 CPU 实现。把它们组合起来就是一份完整的组成原理课设方案再配合附录里的 MIPS 指令集和 CP0 寄存器说明答辩材料的基本盘就已经到位了。从那次课程设计之后我每次做完实验都会强制自己走一遍完整流程新建工程 → 添加源文件 → 约束引脚 → 功能仿真 → 上板验证 → 截图留证。这套流程看起来繁琐但它能防止在最关键的上板环节才发现仿真时忽略的端口名错误或引脚约束遗漏。希望这份实验手册的解读能帮你在做 CPU 实验和课设时少走弯路。本文还有配套的精品资源点击获取