• 正文
  • 相关推荐
申请入驻 产业图谱

embeddedsw:串口不打印、中断不响应,答案都在这份 BSP 源码里

11/03 09:07
37
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

你天天调用的 XUartPs_SendByte,不是什么黑盒函数,就是一行往固定地址写字节的 C 代码。整个 BSP 的源码,全在这一个仓库里。

01为什么第二期写它

因为在 PYNQ 那层"魔法"之下,藏着的东西就是这个仓库。

第一篇讲 PYNQ 时我说过,它让你不用写 C。但只要你往下走一步——换块板子、加个中断、把某个外设初始化改快一点——你就必须回到 C 这一层。而这一层最大的问题不是难,是不透明。

Vitis 点几下鼠标,bsp 目录就出现了,几千行代码,目录还挺长。大部分人从此只敢改 helloworld.c,别的一概不碰。遇到"串口收不到数据""中断进不去""定时器不准",只能去论坛搜,搜到一段看不懂的代码贴进去试。

其实这套东西一点不神秘。它就是一个普通的 C 库:一堆驱动源文件、一张配置表、一组寄存器地址宏。你完全可以脱离 Vitis,用 make 加一个 arm-none-eabi-gcc 编出来。

02它到底是什么

https://github.com/Xilinx/embeddedsw

三个目录,分工非常清楚:

目录 放什么 你会不会碰
lib/bsp/standalone/src/ 启动代码、异常向量、xil_printf、xil_io 几乎不碰,排障必看
lib/sw_services/ 独立软件服务:定时器、电源管理、mailbox 偶尔看
XilinxProcessorIPLib/drivers/ 每个 IP 一个目录的驱动 天天用,最该看

真正的主角是第三行。里面每个 IP 一个文件夹,比如 uartps(PS 端 UART)、scugic(中断控制器)、axidma(DMA)、emacps(以太网)、gpiops(PS 的 GPIO)。

每个驱动目录里,文件名几乎是固定的套路:

•xuartps.c —— 函数实现

•xuartps.h —— 对外 API 和结构体

•xuartps_hw.h —— 寄存器偏移和位定义

•xuartps_g.c —— 配置表:每个实例的基地址、时钟频率、ID

•xuartps_sinit.c —— 按 ID 查配置

•xuartps_intr.c —— 中断处理

看懂这五个文件的关系,整个 BSP 就通了。

03值得读的三处

▍xuartps_g.c —— 你要找的"那个地址"在这里

xparameters.h 里那些 XPAR_XUARTPS_0_BASEADDR 不是凭空来的,它们是从 _g.c 这张表里生成的。

以 Zynq-7000 为例,UART0 的基地址是 0xE0000000,UART1 是 0xE0001000。这两个数字来自《Zynq-7000 TRM》的 memory map,Vivado 生成 BSP 时把 PS 配置写进这张表,xparameters.h 再把表里的值变成宏。

所以下次你怀疑"地址对不对",别猜。打开 xuartps_g.c,看 XUartPs_ConfigTable[] 第一项的 BaseAddress,那就是真的。注意它是生成的,改了会被 Vitis 覆盖,要改配置请去改 .xsa。

▍xuartps_hw.h —— 寄存器级的所有细节

这里定义了 XUARTPS_FIFO_OFFSET、XUARTPS_SR_OFFSET、XUARTPS_CR_OFFSET 这些偏移量,还有每一位的含义,比如 XUARTPS_SR_TXEMPTY(发送 FIFO 空)、XUARTPS_CR_TX_EN(发送使能)。

你想知道"为什么我发了数据但串口助手没显示",从这里开始查最快:先看 SR 的 TXEMPTY 位起没起,再看 CR 的使能位。比在论坛问三轮快得多。

▍lib/bsp/standalone/src/ —— 上电后最先跑的那几百行

boot.S 建立栈和异常向量,xil-crt0.S 跳到 main。中间有个很多人不知道的环节:Xil_ExceptionInit() 必须在用中断前调用,否则中断一来就飞。

还有 xil_printf.c。它和标准 printf 不是一回事——不支持浮点、格式串支持很有限、缓冲区很小。你在裸机里 printf("%f", x) 输出一串乱码,不是串口坏了,是这个函数不支持。

04上手:脱离 Vitis 编一个裸机程序

这是我觉得最值得做的一次练习,做完你对 BSP 的敬畏会消失。

git clone https://github.com/Xilinx/embeddedsw
cd embeddedsw
git checkout v2023.2        # 和你的 Vivado 版本对齐

# 只需要三样:启动代码、UART 驱动、一个 main
arm-none-eabi-gcc -mcpu=cortex-a9 -mfpu=vfpv3
-I lib/bsp/standalone/src/arm/cortexa9
-I XilinxProcessorIPLib/drivers/uartps/src
main.c lib/bsp/standalone/src/arm/cortexa9/gcc/startup.S
XilinxProcessorIPLib/drivers/uartps/src/xuartps.c
XilinxProcessorIPLib/drivers/uartps/src/xuartps_g.c
-T lscript.ld -o demo.elf

编完 arm-none-eabi-objcopy -O binary demo.elf demo.bin,用 bootgen 打成 BOOT.BIN,丢进 SD 卡。

这条路跑通一次,你就再不会怕"BSP 生成失败"——里面根本没有魔法。

05三个坑

一、重新生成 BSP 会覆盖你的修改。所有改在 bsp/ 里的代码都是临时的。要定制,用 _config 宏,或者把驱动目录复制出来改。

二、xparameters.h 里的宏名带实例后缀。XPAR_XUARTPS_0_DEVICE_ID 和 XPAR_XUARTPS_1_DEVICE_ID 是两个不同实例,混用会打到错误的串口,而且不报错。

三、中断不生效,九成是这三个原因之一:GIC 没初始化(XScuGic_CfgInitialize 之前必须 XScuGic_LookupConfig)、中断优先级掩码没设、handler 里没清中断标志。第三条最坑——不清标志,中断只进一次。

06它不适合谁

不适合只想"学硬件"的人。

这个仓库是纯软件视角:它假设硬件已经配好了,只告诉你"怎么操作"。你要想搞明白 AXI 时序、时钟域、复位顺序,得去看 PL 那边的文档。

但如果你想知道"为什么板子行为和我的代码不一致",这里最快——最终执行你意图的,就是这几千行 C。

07写在最后

BSP 不是厂商给你的恩惠,它是你自己代码的一部分,只不过别人先写好了。

把它当源码读,而不是当黑盒用,你对整块芯片的掌控感会立刻不一样。

赛灵思

赛灵思

赛灵思(英语:Xilinx)是一家位于美国的可编程逻辑器件的生产商。该公司发明了现场可编程逻辑门阵列,并由此成名。赛灵思还是第一个无厂半导体公司(Fabless)。28nm时代,赛灵思提出All Programmable 的概念,从单一的FPGA企业战略转型为All Programmable FPGA、 SoC 和 3D IC 的全球领先提供商。且行业领先的器件与新一代设计环境以及 IP 完美地整合在一起,可满足客户对可编程逻辑乃至可编程系统集成的广泛需求赛灵思于1984年创建于美国加利福尼亚州的硅谷,总部位于硅谷核心的圣何塞,并在科罗拉多州、爱尔兰、新加坡 印度、中国、日本拥有分支机构

赛灵思(英语:Xilinx)是一家位于美国的可编程逻辑器件的生产商。该公司发明了现场可编程逻辑门阵列,并由此成名。赛灵思还是第一个无厂半导体公司(Fabless)。28nm时代,赛灵思提出All Programmable 的概念,从单一的FPGA企业战略转型为All Programmable FPGA、 SoC 和 3D IC 的全球领先提供商。且行业领先的器件与新一代设计环境以及 IP 完美地整合在一起,可满足客户对可编程逻辑乃至可编程系统集成的广泛需求赛灵思于1984年创建于美国加利福尼亚州的硅谷,总部位于硅谷核心的圣何塞,并在科罗拉多州、爱尔兰、新加坡 印度、中国、日本拥有分支机构收起

查看更多

相关推荐