用Vivado仿真一个LoongArch单周期CPU从斐波那契数列看CPU如何执行指令计算机体系结构的学习往往从理论开始但真正理解CPU如何工作最好的方式莫过于亲手实现一个简单的处理器并观察它的运行。本文将带你通过Vivado仿真环境深入剖析一个基于LoongArch指令集的单周期CPU执行斐波那契数列计算的全过程。不同于教科书上的抽象描述我们将通过真实的波形图和寄存器变化直观展示指令如何在硬件层面被处理。1. 环境准备与项目概览在开始之前我们需要准备好开发环境。本次实验基于Vivado设计套件使用Verilog HDL语言实现一个精简的LoongArch单周期CPU。这个CPU仅支持5条基本指令但足以完成斐波那契数列的计算任务。实验环境包含以下关键组件miniCPU工程位于minicpu_env/soc_verify/run_vivado/目录下指令存储器初始化文件minicpu_env/func/inst_ram.coe测试平台文件minicpu_env/soc_verify/testbench/minicpu_tb.v提示如果首次使用Vivado建议先熟悉基本操作流程包括工程创建、文件添加、仿真运行等步骤。2. 斐波那契程序与指令解析我们的测试程序计算斐波那契数列这是一个经典的递归序列定义如下f(0) 0f(1) 1f(n) f(n-1) f(n-2) (n ≥ 2)对应的LoongArch汇编代码如下1c000000: addi.w $t0,$zero,0x0 # 初始化f(0)0 1c000004: addi.w $t1,$zero,0x1 # 初始化f(1)1 1c000008: addi.w $s0,$zero,0x0 # 循环变量i0 1c00000c: addi.w $s1,$zero,0x1 # 循环步长1 1c000010: ld.w $a0,$zero,1024 # 读取输入n值 loop: 1c000014: add.w $t2,$t0,$t1 # f(i)f(i-2)f(i-1) 1c000018: addi.w $t0,$t1,0x0 # 更新f(i-2)f(i-1) 1c00001c: addi.w $t1,$t2,0x0 # 更新f(i-1)f(i) 1c000020: add.w $s0,$s0,$s1 # i 1c000024: bne $s0,$a0,loop # 如果i≠n继续循环 1c000028: st.w $t2,$zero,1028 # 存储结果f(n) end: 1c00002c: bne $s1,$zero,end # 无限循环每条指令都对应着CPU内部特定的硬件操作。接下来我们将分解这些指令在单周期CPU中的执行过程。3. 单周期CPU执行流程详解单周期CPU的特点是每条指令在一个时钟周期内完成所有操作。我们的LoongArch CPU包含以下关键部件部件名称功能描述程序计数器(PC)保存下一条指令的地址指令存储器存储程序指令寄存器文件提供32个通用寄存器算术逻辑单元执行算术和逻辑运算数据存储器存储和加载数据控制单元根据指令生成控制信号3.1 取指阶段CPU执行的第一步是从指令存储器中获取当前PC指向的指令。以第一条指令addi.w $t0,$zero,0x0为例PC初始值为0x1c000000CPU将该地址发送到指令存储器指令存储器返回32位指令字在Vivado仿真波形中我们可以看到inst_sram_addr和inst_sram_rdata信号的变化。3.2 译码阶段获取指令后CPU需要解析指令字段并生成控制信号。LoongArch指令格式主要包含以下字段opcode指令操作码31:26位rj/rk/rd寄存器操作数9:5,14:10,4:0位立即数指令中的常数部分对于addi.w指令控制单元会生成以下信号inst_addi_w 1b1; // 标识当前是addi指令 src2_is_imm 1b1; // 第二个操作数是立即数 gr_we 1b1; // 需要写回寄存器3.3 执行阶段在执行阶段ALU根据控制信号执行相应操作。对于addi.w $t0,$zero,0x0从寄存器文件读取$zero的值恒为0将12位立即数符号扩展为32位ALU执行加法操作0 0 0在波形图中可以观察到alu_src1、alu_src2和alu_result信号的变化。3.4 访存阶段对于加载(ld.w)和存储(st.w)指令CPU需要访问数据存储器。例如ld.w $a0,$zero,1024计算内存地址$zero 1024 1024从地址1024读取数据到$a0在测试平台中这个地址对应拨码开关的输入值。3.5 写回阶段最后需要将结果写回寄存器文件。对于算术指令结果来自ALU对于加载指令结果来自数据存储器。assign rf_wdata res_from_mem ? data_sram_rdata : alu_result;4. Vivado仿真与波形分析运行Vivado仿真后我们可以观察CPU执行斐波那契程序的全过程。重点关注以下信号时钟信号(clk)驱动CPU的基准时钟复位信号(resetn)系统复位控制PC值显示当前执行指令地址指令(inst)当前执行的指令内容寄存器值如$t0、$t1、$t2等关键寄存器ALU结果算术运算的输出通过逐步分析波形可以清晰地看到初始化阶段$t0和$t1被设置为0和1循环阶段$t2不断更新为前两项之和结果存储最终结果被写入内存特定位置注意在仿真时可以通过修改minicpu_tb.v中的switch值来改变输入参数n观察不同n值时的计算过程。5. 性能优化与扩展思考虽然单周期CPU设计简单直观但它有明显的性能限制——所有指令都必须在一个时钟周期内完成这导致时钟频率受最慢指令限制。在实际项目中我们通常会考虑以下优化方向多周期设计将指令执行分为多个阶段提高时钟频率流水线技术重叠执行多条指令提高吞吐量指令集扩展增加更多实用指令如乘法、移位等例如将我们的单周期CPU改造为五级流水线可以显著提高性能// 简化的流水线寄存器示例 always (posedge clk) begin if (~stall) begin IF_ID {pc_plus_4, inst}; ID_EX {reg1, reg2, imm, rd, ...}; EX_MEM {alu_result, write_data, ...}; MEM_WB {mem_data, alu_result, ...}; end end在实现这些优化时需要特别注意数据冒险和控制冒险的处理这可以通过前递(forwarding)和流水线暂停(stalling)等技术来解决。