news 2026/5/27 8:23:59

新手也能懂:用Verilog手搓一个APB总线读写控制器(附完整代码)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
新手也能懂:用Verilog手搓一个APB总线读写控制器(附完整代码)

从零构建APB总线控制器:Verilog实战指南

在数字集成电路设计中,AMBA总线协议是连接处理器与外围设备的重要桥梁。对于初学者而言,直接阅读协议文档往往如同面对天书——各种时序图、状态转换和信号交互让人望而生畏。本文采用"代码先行"的逆向学习法,通过动手实现一个完整的APB Master控制器,带你直观理解总线通信的核心机制。我们将从最基础的信号定义开始,逐步构建出能够自动处理IDLE、SETUP、ENABLE三阶段状态机的智能控制器,并在最后给出可直接复用的完整代码。

1. APB总线基础与设计准备

APB(Advanced Peripheral Bus)作为AMBA协议家族中的轻量级成员,以其简单的两相握手机制著称。它特别适合低速外设连接,典型应用包括GPIO、UART等控制寄存器访问。与AHB/AXI等高性能总线不同,APB采用非流水线设计,每次传输至少需要两个时钟周期。

开始编码前,我们需要明确几个关键信号的作用:

  • PCLK:总线时钟,所有信号在上升沿采样
  • PSELx:从设备选择信号(x表示第几个从设备)
  • PENABLE:传输使能信号
  • PADDR:32位地址总线
  • PWRITE:读写方向控制(1=写,0=读)
  • PWDATA:写数据总线(Master→Slave)
  • PRDATA:读数据总线(Slave→Master)
  • PREADY:从设备准备就绪信号

提示:APB协议规定,当PREADY为低时,主设备必须保持当前传输状态,直到从设备拉高PREADY表示可以继续。

为构建我们的控制器,先创建基础模块框架:

module apb_master ( input wire PCLK, input wire PRESETn, output reg [31:0] PADDR, output reg PWRITE, output reg [31:0] PWDATA, output reg PSEL, output reg PENABLE, input wire [31:0] PRDATA, input wire PREADY ); // 状态机定义 parameter IDLE = 2'b00; parameter SETUP = 2'b01; parameter ENABLE = 2'b10; reg [1:0] state; // 后续代码将在此补充 endmodule

2. 状态机设计与实现

APB协议的精髓在于其三阶段状态转换机制。理解这个状态机是掌握APB通信的关键。我们采用Moore型状态机实现,其输出仅取决于当前状态。

2.1 状态转移逻辑

状态机的Verilog实现需要考虑所有可能的转换路径:

always @(posedge PCLK or negedge PRESETn) begin if (!PRESETn) begin state <= IDLE; PSEL <= 1'b0; PENABLE <= 1'b0; end else begin case (state) IDLE: begin if (transfer_request) begin // 有传输请求时进入SETUP state <= SETUP; PSEL <= 1'b1; PADDR <= target_addr; PWRITE <= write_en; if (write_en) PWDATA <= write_data; end end SETUP: begin state <= ENABLE; PENABLE <= 1'b1; end ENABLE: begin if (PREADY) begin // 从设备就绪后返回IDLE state <= IDLE; PSEL <= 1'b0; PENABLE <= 1'b0; end end endcase end end

这个状态机完美体现了APB协议的三个关键阶段:

  1. IDLE:初始状态,无传输活动
  2. SETUP:选中从设备,建立地址和写数据(如果需要)
  3. ENABLE:使能传输,等待从设备响应

2.2 PREADY等待机制处理

实际应用中,从设备可能需要多个周期才能准备好数据。我们的控制器必须正确处理这种情况:

// 在ENABLE状态中扩展等待逻辑 ENABLE: begin if (PREADY) begin state <= IDLE; PSEL <= 1'b0; PENABLE <= 1'b0; if (!PWRITE) read_data <= PRDATA; // 捕获读取的数据 end else begin // 保持当前状态和信号不变 state <= ENABLE; end end

注意:在FPGA原型验证时,建议添加超时计数器防止死锁,但实际ASIC设计中通常不需要,因为从设备必须保证最终会响应。

3. 完整控制器实现与接口封装

为了让控制器更实用,我们添加传输请求接口和完成指示信号。完整实现如下:

module apb_master ( input wire PCLK, input wire PRESETn, // APB接口 output reg [31:0] PADDR, output reg PWRITE, output reg [31:0] PWDATA, output reg PSEL, output reg PENABLE, input wire [31:0] PRDATA, input wire PREADY, // 用户接口 input wire start, input wire [31:0] addr, input wire wr_en, input wire [31:0] wdata, output reg done, output reg [31:0] rdata ); // 状态定义 parameter IDLE = 2'b00; parameter SETUP = 2'b01; parameter ENABLE = 2'b10; reg [1:0] state; reg transfer_request; always @(posedge PCLK or negedge PRESETn) begin if (!PRESETn) begin state <= IDLE; PSEL <= 1'b0; PENABLE <= 1'b0; done <= 1'b0; transfer_request <= 1'b0; end else begin done <= 1'b0; // 默认done为0 case (state) IDLE: begin if (transfer_request) begin state <= SETUP; PSEL <= 1'b1; PADDR <= addr; PWRITE <= wr_en; if (wr_en) PWDATA <= wdata; end else if (start) begin transfer_request <= 1'b1; end end SETUP: begin state <= ENABLE; PENABLE <= 1'b1; end ENABLE: begin if (PREADY) begin state <= IDLE; PSEL <= 1'b0; PENABLE <= 1'b0; transfer_request <= 1'b0; done <= 1'b1; if (!PWRITE) rdata <= PRDATA; end end endcase end end endmodule

这个增强版控制器提供了更友好的用户接口:

  • start:启动传输脉冲信号
  • addr:目标地址
  • wr_en:写使能
  • wdata:写数据(写操作时有效)
  • done:传输完成指示
  • rdata:读取的数据(读操作时有效)

4. 仿真验证与调试技巧

任何RTL设计都需要充分的验证。我们使用简单的测试平台来验证控制器的正确性。

4.1 测试平台搭建

module apb_master_tb; reg PCLK; reg PRESETn; wire [31:0] PADDR; wire PWRITE; wire [31:0] PWDATA; wire PSEL; wire PENABLE; reg [31:0] PRDATA; reg PREADY; reg start; reg [31:0] addr; reg wr_en; reg [31:0] wdata; wire done; wire [31:0] rdata; // 实例化被测模块 apb_master uut (.*); // 时钟生成 always #5 PCLK = ~PCLK; initial begin // 初始化 PCLK = 0; PRESETn = 0; PRDATA = 32'h0; PREADY = 1; start = 0; addr = 32'h0; wr_en = 0; wdata = 32'h0; // 复位释放 #20 PRESETn = 1; // 测试写操作 #10 start = 1; addr = 32'h4000_0000; wr_en = 1; wdata = 32'h1234_5678; #10 start = 0; // 等待写完成 wait(done); // 测试读操作 #20 start = 1; addr = 32'h4000_0004; wr_en = 0; PRDATA = 32'h9ABC_DEF0; // 从设备返回的测试数据 #10 start = 0; // 模拟从设备延迟响应 #10 PREADY = 0; #30 PREADY = 1; // 等待读完成并检查rdata wait(done); if (rdata !== 32'h9ABC_DEF0) $display("读数据错误!"); #100 $finish; end endmodule

4.2 常见问题排查

调试APB控制器时,以下几个信号值得特别关注:

  • 状态机卡死:检查PREADY是否被正确处理,从设备是否真的会拉高PREADY
  • 数据错误
    • 写操作:确认PWDATA在SETUP阶段已经稳定
    • 读操作:检查PRDATA是否在PENABLE和PREADY同时为高时采样
  • 时序违规
    • PSEL和PENABLE必须严格遵循协议时序
    • PADDR/PWDATA在SETUP阶段必须保持稳定直到传输完成

提示:在仿真波形中,建议将state信号以符号形式显示(IDLE/SETUP/ENABLE),这样更直观。

5. 进阶优化与扩展

基础控制器工作正常后,可以考虑以下增强功能:

5.1 多从设备支持

实际系统通常需要访问多个从设备。扩展PSEL为位向量,并添加地址解码逻辑:

// 在模块端口声明中修改 output reg [3:0] PSEL, // 支持最多4个从设备 // 在地址解码部分 always @(*) begin PSEL = 4'b0000; // 默认不选中任何设备 if (state != IDLE) begin case (PADDR[31:28]) 4'h4: PSEL = 4'b0001; // 地址0x4000_0000~0x4FFF_FFFF 4'h5: PSEL = 4'b0010; // 地址0x5000_0000~0x5FFF_FFFF // 其他地址空间... endcase end end

5.2 突发传输支持

虽然标准APB不支持突发传输,但可以通过添加传输计数器实现类似功能:

// 添加突发长度寄存器 reg [3:0] burst_count; reg [31:0] base_addr; // 在状态机中处理突发 if (PREADY && burst_count > 0) begin burst_count <= burst_count - 1; PADDR <= PADDR + 4; // 32位字地址递增 if (!PWRITE) rdata[burst_count] <= PRDATA; // 存储读取的数据 end else if (PREADY) begin state <= IDLE; // ...其他清理代码 end

5.3 性能监控

添加性能计数器来监测总线利用率:

reg [31:0] clock_counter; reg [31:0] busy_counter; always @(posedge PCLK) begin clock_counter <= clock_counter + 1; if (state != IDLE) busy_counter <= busy_counter + 1; end // 计算利用率 wire [31:0] utilization = (busy_counter * 100) / clock_counter;

这些扩展功能可以根据实际需求选择性实现。在资源受限的设计中,建议从最简单的版本开始,逐步添加必要功能。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/27 8:21:47

如何完整备份QQ空间数据:面向普通用户的终极本地存储解决方案

如何完整备份QQ空间数据&#xff1a;面向普通用户的终极本地存储解决方案 【免费下载链接】QZoneExport QQ空间导出助手&#xff0c;用于备份QQ空间的说说、日志、私密日记、相册、视频、留言板、QQ好友、收藏夹、分享、最近访客为文件&#xff0c;便于迁移与保存 项目地址: …

作者头像 李华
网站建设 2026/5/27 8:20:21

别再只把Ettercap当嗅探器了:实战演示如何用它快速排查内网ARP欺骗攻击

企业内网安全实战&#xff1a;用Ettercap精准定位ARP欺骗攻击源当企业内网突然出现大面积断网、网速异常波动或敏感数据泄露迹象时&#xff0c;安全运维团队往往面临巨大压力。传统排查手段如逐一检查交换机端口或分析防火墙日志效率低下&#xff0c;而专业的商业安全设备又存在…

作者头像 李华
网站建设 2026/5/27 8:14:13

《C语言学习:编程例题》B

写在前面&#xff1a;本笔记为个人学习各平台C语言系列课程所作&#xff0c;仅供交流学习&#xff0c;不得作他用。1. 字符型多次读入/getchar函数#include <stdio.h>int main() { char c; int letter 0, blank 0, digit 0, other 0; int count 0;while (c…

作者头像 李华
网站建设 2026/5/27 8:14:10

从结构化到面向对象:系统架构设计方法的核心演进

在系统架构设计师的考试体系中&#xff0c;“设计方法”是决定软件系统质量与可维护性的关键内容。当前主流的两种设计方法——结构化分析与设计、面向对象的分析设计&#xff0c;分别代表了不同时代、不同场景下的工程化思维。理解并掌握它们&#xff0c;是架构师必备的能力。…

作者头像 李华