手把手教你用Vivado 2023.1在Zynq上搭建ARM Cortex-M3软核(附Jlink调试与点灯实战)
引言
在嵌入式系统开发领域,FPGA与ARM处理器的结合正变得越来越普遍。Xilinx Zynq系列SoC凭借其独特的FPGA+ARM架构,为开发者提供了极大的灵活性。本文将带你从零开始,在Vivado 2023.1环境中为Zynq FPGA构建一个完整的Cortex-M3软核系统。
不同于传统的硬核处理器,软核处理器给了开发者更多定制化的可能。你可以根据项目需求调整内存大小、外设接口甚至指令集扩展。我们将使用ARM官方提供的Cortex-M3 DesignStart IP,通过AXI总线连接必要的外设,最终实现一个可调试、可编程的最小系统。
1. 环境准备与IP核获取
1.1 硬件需求清单
- 开发板:Xilinx Zynq系列(如ZedBoard、PYNQ-Z2等),建议选择Artix-7或Zynq-7000系列
- 调试工具:Jlink调试器(支持SWD模式)
- 其他:USB转串口模块(可选,用于调试输出)
1.2 软件工具准备
| 工具名称 | 版本要求 | 用途说明 |
|---|---|---|
| Vivado | 2023.1 | FPGA设计与综合 |
| Keil MDK | 5.38+ | ARM嵌入式开发环境 |
| Jlink驱动 | 最新版 | 调试器支持 |
1.3 ARM DesignStart IP获取
- 访问ARM DesignStart官网注册账号
- 下载Cortex-M3 DesignStart评估包
- 解压后找到
Arm_ipi_repository文件夹,这是我们将要使用的IP核源
提示:ARM DesignStart项目提供免费的Cortex-M系列处理器IP,但商业用途需要获得授权。
2. Vivado工程配置
2.1 创建基础工程
# 创建新工程命令示例 create_project cortex_m3_softcore /path/to/project -part xc7z020clg400-1 set_property board_part em.avnet.com:zed:part0:1.4 [current_project]2.2 添加IP核仓库
- 在Vivado界面选择Tools > Settings
- 导航到IP > Repository
- 添加之前下载的
Arm_ipi_repository文件夹路径
2.3 Block Design初始化
# 创建Block Design create_bd_design "cortex_m3_system"3. Cortex-M3软核系统搭建
3.1 添加并配置Cortex-M3 IP核
在Block Design中添加ARM Cortex-M3处理器IP,关键配置参数如下:
- 时钟频率:设置为50MHz(适合大多数应用场景)
- 调试接口:选择SWD模式(与Jlink兼容)
- 内存配置:
- ITCM:32KB
- DTCM:32KB
- 取消Initialize选项
3.2 时钟网络设计
使用Clocking Wizard IP生成系统时钟:
// 时钟约束示例 create_clock -period 20.000 -name clk [get_ports clk_in]配置参数:
- 输入时钟:板载晶振频率(通常50MHz或100MHz)
- 输出时钟:50MHz(供Cortex-M3使用)
- 启用locked输出信号
3.3 复位系统设计
复位网络是软核稳定运行的关键。推荐使用Processor System Reset IP:
- 连接时钟信号到
slowest_sync_clk - 连接Clocking Wizard的locked信号到
dcm_locked - 输出复位信号:
mb_reset→ Cortex-M3系统复位(需反相)interconnect_aresetn→ AXI总线复位peripheral_aresetn→ 外设复位
3.4 AXI总线互联
添加AXI Interconnect IP连接处理器与外设:
- 设置1个主端口(Cortex-M3)和2个从端口(GPIO、UART)
- 时钟连接:主时钟50MHz
- 复位连接:使用Processor System Reset的输出
注意:AXI总线地址会自动分配,但建议手动检查确保没有冲突。
4. 调试接口与GPIO实现
4.1 SWD调试接口设计
Jlink SWD接口需要特殊处理,创建自定义模块实现双向IO:
module swd_interface( input swclk, input swdio_out, output swdio_in, input swdio_oe, inout swdio_pad ); IOBUF swd_iobuf ( .O(swdio_in), .I(swdio_out), .IO(swdio_pad), .T(~swdio_oe) ); endmodule关键约束:
- SWCLK必须分配到全局时钟引脚
- 在XDC文件中添加如下约束:
set_property PACKAGE_PIN F20 [get_ports swclk] set_property IOSTANDARD LVCMOS33 [get_ports swclk]4.2 GPIO外设添加
- 添加AXI GPIO IP核
- 配置:
- 数据宽度:32位
- 启用中断(可选)
- 分配地址空间:建议0x40000000开始
- 连接外部LED引脚并添加约束
5. Keil工程配置与调试
5.1 基础工程设置
- 创建新工程,选择Cortex-M3设备
- 配置目标选项:
- ROM地址:0x00000000(ITCM起始地址)
- RAM地址:0x20000000(DTCM起始地址)
- 大小:与Vivado中配置一致
5.2 Jlink调试配置
- 选择SWD接口模式
- 设置调试速度为1MHz(初始可降低速度确保稳定性)
- 添加自定义Flash下载算法(针对DTCM)
5.3 点灯程序实现
#include "DS_CM3.h" #define GPIO_BASE 0x40000000 #define GPIO_DATA_OFFSET 0x00 void delay_ms(uint32_t ms) { uint32_t ticks = ms * (SystemCoreClock / 1000); uint32_t start = DWT->CYCCNT; while((DWT->CYCCNT - start) < ticks); } int main(void) { // 启用GPIO *(volatile uint32_t *)(GPIO_BASE + GPIO_DATA_OFFSET) = 0x01; while(1) { // LED闪烁 *(volatile uint32_t *)(GPIO_BASE + GPIO_DATA_OFFSET) ^= 0x01; delay_ms(500); } }6. 常见问题排查
6.1 软核无法启动
- 检查复位信号是否正常(应保持低电平有效)
- 验证时钟信号是否稳定(使用示波器测量)
- 确认SWD接口连接正确
6.2 Jlink连接失败
- 检查硬件连接:
- SWCLK → FPGA时钟引脚
- SWDIO → 双向IO引脚
- 尝试降低SWD时钟频率
- 确认FPGA已正确加载bitstream
6.3 GPIO无输出
- 验证Vivado中的引脚约束是否正确
- 检查Keil工程中的地址映射是否与Vivado一致
- 确保GPIO IP核时钟和复位信号已正确连接
7. 系统扩展建议
7.1 添加更多外设
- UART:用于调试信息输出
- 定时器:精确时间控制
- 中断控制器:管理多个中断源
7.2 性能优化技巧
- 启用ITCM/DTCM的ECC保护(提高可靠性)
- 调整AXI总线位宽(32位→64位提升吞吐量)
- 添加指令缓存(针对频繁执行的代码)
7.3 自定义外设开发
通过AXI Lite接口添加自定义IP:
- 使用Vivado的IP打包工具创建新IP
- 实现寄存器映射逻辑
- 添加到Block Design并连接中断(如果需要)
在实际项目中,我遇到过因复位信号异步释放导致系统不稳定的情况。解决方法是在Verilog中添加同步复位处理电路,确保所有触发器在同一时钟边沿释放复位。