APB总线实战:在FPGA上搭建一个简易SoC,用APB配置PWM和外设(Vivado工程分享)

张开发
2026/4/11 8:19:24 15 分钟阅读

分享文章

APB总线实战:在FPGA上搭建一个简易SoC,用APB配置PWM和外设(Vivado工程分享)
APB总线实战在FPGA上搭建一个简易SoC用APB配置PWM和外设Vivado工程分享在嵌入式系统设计中总线架构的选择直接影响着系统的性能和可扩展性。AMBA APBAdvanced Peripheral Bus作为ARM公司推出的低功耗、低带宽总线协议特别适合用于配置寄存器和控制低速外设。本文将带您从零开始在Xilinx Vivado平台上构建一个基于APB总线的简易SoC系统实现PWM控制器和GPIO模块的集成与控制。1. 系统架构设计与规划一个典型的APB总线系统包含三个核心组件主设备Master、从设备Slave和互联结构。在我们的设计中将采用以下架构主设备使用ARM Cortex-M0软核处理器作为APB Master从设备PWM控制器0x40000000 - 0x4000FFFFGPIO模块0x40010000 - 0x4001FFFFUART控制器0x40020000 - 0x4002FFFF系统时钟50MHz主时钟APB总线同步时钟地址映射表如下外设模块基地址地址范围功能描述PWM0x4000000064KB脉冲宽度调制控制器GPIO0x4001000064KB通用输入输出接口UART0x4002000064KB串行通信接口2. Vivado工程创建与IP集成首先在Vivado中创建新工程选择对应的FPGA器件如Artix-7 xc7a35t。然后按照以下步骤进行IP集成# 创建新工程 create_project apb_soc ./apb_soc -part xc7a35tftg256-1 # 添加Cortex-M0处理器IP create_ip -name cortexm0ds -vendor arm.com -library processor -version 1.0 -module_name cortexm0_0PWM控制器的APB接口设计要点module pwm_apb #( parameter ADDR_WIDTH 16, parameter DATA_WIDTH 32 )( // APB接口信号 input pclk, input presetn, input [ADDR_WIDTH-1:0] paddr, input psel, input penable, input pwrite, input [DATA_WIDTH-1:0] pwdata, output [DATA_WIDTH-1:0] prdata, output pready, // PWM输出信号 output pwm_out ); // 寄存器定义 reg [31:0] period_reg; reg [31:0] duty_reg; reg [1:0] ctrl_reg; // APB状态机 typedef enum {IDLE, SETUP, ACCESS} apb_state_t; apb_state_t current_state, next_state; // PWM生成逻辑 always (posedge pclk or negedge presetn) begin if (!presetn) begin // 复位逻辑 end else begin // PWM波形生成 end end // APB接口逻辑 always (posedge pclk or negedge presetn) begin if (!presetn) begin // 复位逻辑 end else begin case(current_state) SETUP: begin if (psel !penable) begin // 准备阶段 end end ACCESS: begin if (psel penable) begin if (pwrite) begin // 写操作 case(paddr[15:0]) 16h0000: period_reg pwdata; 16h0004: duty_reg pwdata; 16h0008: ctrl_reg pwdata[1:0]; endcase end else begin // 读操作 case(paddr[15:0]) 16h0000: prdata period_reg; 16h0004: prdata duty_reg; 16h0008: prdata {30b0, ctrl_reg}; endcase end end end endcase end end assign pready (current_state ACCESS); assign pwm_out ...; // PWM输出逻辑 endmodule3. APB总线互联与地址解码在Vivado中使用AXI Interconnect IP来自动生成APB互联结构或者手动实现地址解码器module apb_decoder ( input [31:0] paddr, output psel_pwm, output psel_gpio, output psel_uart ); assign psel_pwm (paddr[31:16] 16h4000); assign psel_gpio (paddr[31:16] 16h4001); assign psel_uart (paddr[31:16] 16h4002); endmodule总线时序验证要点写操作时序主设备在SETUP阶段置位PSELx设置PADDR、PWRITE1和PWDATA在ACCESS阶段置位PENABLE保持其他信号稳定从设备在ACCESS阶段检测到PSELx和PENABLE为高时采样数据并置位PREADY读操作时序主设备在SETUP阶段置位PSELx设置PADDR和PWRITE0在ACCESS阶段置位PENABLE从设备在ACCESS阶段返回PRDATA并置位PREADY注意APB协议要求所有信号在PENABLE为高时必须保持稳定直到PREADY为高。4. 系统验证与调试技巧完成硬件设计后需要验证APB总线的正确性和外设功能。推荐采用以下验证流程寄存器读写测试// 测试PWM周期寄存器读写 uint32_t test_value 0x12345678; *(volatile uint32_t *)0x40000000 test_value; // 写操作 uint32_t read_back *(volatile uint32_t *)0x40000000; // 读操作 if (read_back ! test_value) { // 错误处理 }PWM波形验证使用逻辑分析仪或示波器观察PWM输出修改占空比寄存器验证波形变化GPIO功能测试// 配置GPIO为输出 *(volatile uint32_t *)0x40010008 0x00000001; // 方向寄存器 *(volatile uint32_t *)0x40010000 0x00000001; // 数据寄存器调试中常见问题及解决方案问题现象可能原因解决方法读回数据全为0地址解码错误检查PSEL信号生成逻辑PREADY始终为低从设备状态机卡死仿真查看从设备状态机写操作不生效寄存器复位值不正确检查寄存器复位逻辑不同外设间干扰地址映射重叠重新规划地址空间在工程实践中我发现在Vivado中使用System ILA核进行实时调试非常有效。以下是一个典型的ILA配置示例# 创建ILA核 create_debug_core ila_apb ila set_property C_DATA_DEPTH 1024 [get_debug_cores ila_apb] set_property C_TRIGIN_EN false [get_debug_cores ila_apb] # 添加APB信号探针 set_property port_width 1 [get_debug_ports ila_apb/clk] set_property port_width 32 [get_debug_ports ila_apb/probe0] set_property PROBE_TYPE DATA_AND_TRIGGER [get_debug_ports ila_apb/*] connect_debug_port ila_apb/clk [get_nets pclk] connect_debug_port ila_apb/probe0 [get_nets {paddr[31:0]}] # 添加更多探针...通过这个完整的APB SoC设计流程我们不仅实现了基本的总线通信功能还构建了一个可扩展的外设框架。在实际项目中可以根据需要添加更多APB从设备如定时器、SPI控制器等只需遵循相同的接口规范即可。

更多文章