在数字系统设计中,总线协议的理解常常让工程师陷入两难:阅读文档时觉得条理清晰,实际调试时却对信号时序束手无策。这种理论与实践的割裂,在AMBA-APB这类基础协议的学习中尤为明显。本文将带你用Verilog实现一个完整的APB Master控制器,通过代码编写和仿真测试,让协议规范中的文字描述转化为可观察的波形信号。
APB Master作为总线事务的发起者,需要精确管理PSEL、PENABLE等关键信号的时序关系。我们先从接口定义开始:
verilog复制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 PREADY,
input wire [31:0] PRDATA,
input wire PSLVERR
);
状态机设计是控制逻辑的核心。APB协议规定的两阶段访问机制(Setup Phase和Access Phase)自然对应三个状态:
verilog复制typedef enum logic [1:0] {
IDLE,
SETUP,
ACCESS
} apb_state_t;
apb_state_t current_state, next_state;
注意:实际工程中建议添加ERROR状态处理异常情况,但基础实现可先通过PSLVERR信号直接反馈错误
每个PCLK上升沿根据当前状态和Slave响应更新状态:
verilog复制always @(posedge PCLK or negedge PRESETn) begin
if (!PRESETn) begin
current_state <= IDLE;
end else begin
current_state <= next_state;
end
end
状态转移条件需要严格遵循协议时序要求:
verilog复制always_comb begin
case (current_state)
IDLE: next_state = start_transfer ? SETUP : IDLE;
SETUP: next_state = ACCESS;
ACCESS: next_state = PREADY ? (has_next ? SETUP : IDLE) : ACCESS;
default:next_state = IDLE;
endcase
end
对应的信号生成逻辑:
| 状态 | PSEL | PENABLE | 其他信号 |
|---|---|---|---|
| IDLE | 0 | 0 | 保持稳定 |
| SETUP | 1 | 0 | 更新PADDR/PWRITE/PWDATA |
| ACCESS | 1 | 1 | 保持信号稳定 |
为模拟真实场景,可增加等待周期计数器:
verilog复制reg [2:0] wait_counter;
always @(posedge PCLK) begin
if (current_state == ACCESS && !PREADY)
wait_counter <= wait_counter + 1;
else
wait_counter <= 0;
end
创建一个可配置响应的Slave模型用于验证:
verilog复制module virtual_apb_slave (
input wire PCLK,
input wire PSEL,
input wire PENABLE,
output reg PREADY = 0,
output reg [31:0] PRDATA,
output reg PSLVERR
);
// 可配置参数
parameter DELAY_CYCLES = 1;
parameter ERROR_RATE = 0;
always @(posedge PCLK) begin
if (PSEL && PENABLE) begin
PREADY <= ($random % DELAY_CYCLES) == 0;
PSLVERR <= ($urandom_range(100) < ERROR_RATE);
end else begin
PREADY <= 0;
PSLVERR <= 0;
end
end
endmodule
通过仿真观察不同场景下的信号变化:
无等待写入时序:
带等待状态的读取:
提示:使用$display在仿真中打印状态转移信息,辅助波形分析
APB4引入的安全特性可通过扩展状态机实现:
verilog复制reg [2:0] PPROT;
always @(*) begin
if (secure_transfer)
PPROT = 3'b001; // 安全数据访问
else
PPROT = 3'b000; // 普通访问
end
verilog复制// 重试计数器示例
reg [3:0] retry_count;
always @(posedge PCLK) begin
if (PSLVERR && retry_count < MAX_RETRY) begin
retry_count <= retry_count + 1;
next_state = SETUP; // 重新发起传输
end
end
在实际FPGA调试中,遇到总线问题时建议:
通过这个APB Master的实现过程,那些原本枯燥的协议条文变成了可观察、可调试的硬件行为。当你能预测每个时钟沿的信号变化时,协议才算真正掌握。