动手调试:在FPGA上用Verilog仿真AHB-Lite总线,理解传输时序(附代码)

张开发
2026/4/17 14:23:21 15 分钟阅读

分享文章

动手调试:在FPGA上用Verilog仿真AHB-Lite总线,理解传输时序(附代码)
动手调试在FPGA上用Verilog仿真AHB-Lite总线理解传输时序附代码第一次接触AHB-Lite总线时看着文档里那些时序图总觉得隔着一层纱。直到在Vivado里亲手搭建了测试环境看着波形图上HREADY信号的跳变才真正明白地址相位和数据相位的关系。如果你也在从理论到实践的路上摸索不妨跟着这个实验把协议可视化。1. 实验环境搭建准备一个最简单的AHB-Lite系统需要以下组件主设备(Master)产生读写请求的模块从设备(Slave)这里用SRAM模型作为示例互联矩阵简化版只需地址译码器测试平台生成时钟、复位和激励信号推荐使用Xilinx Vivado 2022.1或Intel Quartus Prime 21.3这两个版本对SystemVerilog的支持都比较完善。新建工程时记得勾选包含仿真库后续波形查看会用到。关键信号连接关系如下表信号名称方向位宽描述HCLK输入1系统时钟HRESETn输入1低电平有效复位HADDR主→从32地址总线HWDATA主→从32写数据HRDATA从→主32读数据HWRITE主→从1写使能HSIZE主→从3传输大小HBURST主→从3突发类型HPROT主→从4保护控制HTRANS主→从2传输类型HREADY从→主1传输完成标志HRESP从→主1响应类型(0OK,1ERR)提示初学者可以先用固定32位数据宽度等基本时序掌握后再尝试支持HSIZE指定的不同位宽传输。2. 主设备状态机实现AHB主设备的核心是一个状态机典型实现包含以下状态typedef enum logic [1:0] { IDLE, // 空闲状态 ADDR_PHASE, // 地址相位 DATA_PHASE // 数据相位 } ahb_state_t;对应的Verilog代码片段always_ff (posedge HCLK or negedge HRESETn) begin if (!HRESETn) begin state IDLE; HTRANS 2b00; // IDLE end else begin case(state) IDLE: begin if (start_transfer) begin HADDR target_addr; HWRITE write_en; HSIZE 3b010; // 32-bit HTRANS 2b10; // NONSEQ state ADDR_PHASE; end end ADDR_PHASE: begin if (HREADY) begin if (HWRITE) HWDATA write_data; state DATA_PHASE; end end DATA_PHASE: begin if (HREADY) begin if (!HWRITE) read_data HRDATA; HTRANS 2b00; // 返回IDLE state IDLE; end end endcase end end这个状态机实现了最基本的单次传输流程从IDLE状态接收到start_transfer信号后进入地址相位在地址相位驱动地址和控制信号当HREADY为高时进入数据相位根据读写类型处理数据总线传输完成后返回IDLE状态3. 从设备SRAM模型设计从设备需要处理的关键场景包括地址译码判断是否响应本次传输读写操作执行生成HREADY和HRESP响应下面是一个简单的SRAM模型实现要点// 存储阵列 logic [31:0] memory [0:1023]; // 4KB SRAM // 地址译码 always_comb begin slave_select (HADDR[31:12] 20h00000); // 只响应0x0000_0000~0x0000_0FFF HREADYOUT 1b1; // 本实现始终就绪 HRESP 1b0; // 始终OK响应 end // 读写处理 always_ff (posedge HCLK) begin if (HSEL HREADY HWRITE) begin memory[HADDR[11:2]] HWDATA; // 字寻址 end end assign HRDATA (HSEL !HWRITE) ? memory[HADDR[11:2]] : 32h0;注意实际工程中应该添加对HSIZE的处理支持字节/半字访问。这里简化实现只处理32位访问。4. 测试平台与波形分析测试平台需要生成时钟、复位信号以及激励序列。下面是一个典型的测试场景initial begin // 初始化 HRESETn 0; start_transfer 0; write_en 0; target_addr 0; write_data 0; // 复位 #100 HRESETn 1; // 写操作测试 #20; start_transfer 1; write_en 1; target_addr 32h0000_0100; write_data 32h1234_5678; // 读操作测试 #40; start_transfer 1; write_en 0; target_addr 32h0000_0100; // 结束 #60 $finish; end在仿真波形中重点关注这些信号的变化关系地址相位HTRANS变为NONSEQ时HADDR和HWRITE等控制信号有效数据相位HREADY为高时主设备在写操作驱动HWDATA从设备在读操作驱动HRDATA传输完成HREADY为高且HRESP为低表示传输成功典型波形特征写操作HADDR稳定后下一个周期HWDATA有效读操作HADDR稳定后从设备在下一个周期返回HRDATA每个传输都包含地址相位和数据相位两个阶段5. 调试技巧与常见问题在实际调试中这些问题最常出现问题1HREADY一直为低检查从设备是否正确地驱动了HREADYOUT确认地址译码正确HSEL信号有效问题2HRDATA显示为X检查从设备的读路径是否完整确认内存数组初始化正确可用$readmemh初始化问题3波形显示传输未完成确认主设备状态机正确转换检查HTRANS信号是否在IDLE状态时为00调试时可以逐步增加复杂度先实现单次32位读写加入对HSIZE的支持8/16/32位实现INCR突发传输添加错误响应处理// 突发传输示例 if (start_burst) begin HADDR base_addr; HBURST 3b001; // INCR4 HTRANS 2b10; // NONSEQ burst_count 0; state ADDR_PHASE; end else if (HREADY burst_count 3) begin HADDR HADDR 4; HTRANS 2b11; // SEQ burst_count burst_count 1; end6. 进阶实验建议掌握基础时序后可以尝试这些扩展实验添加多个从设备实现地址译码实现带等待状态的从设备HREADY可控添加HRESP错误响应处理对比AHB-Lite与完整AHB的区别在真实FPGA上运行需添加时钟分频一个实用的调试技巧是在Testbench中加入自动检查always (posedge HCLK) begin if (HREADY !HWRITE HADDR 32h100) begin if (HRDATA ! expected_data) begin $error(Data mismatch! Got %h, expected %h, HRDATA, expected_data); end end end当你在波形图上看到HADDR、HWDATA、HRDATA这些信号按照协议规范准确跳变时那种顿悟的感觉正是硬件调试的魅力所在。记得第一次成功捕获到带等待状态的传输波形时我对着屏幕研究了整整半小时——纸上得来终觉浅绝知此事要躬行。

更多文章