
在Zynq平台上做Petalinux开发时AXI UART Lite大概是那种“越简单越容易栽跟头”的外设。从Vivado里拖出这个IP、分配好地址最多十分钟可等到板子上Linux跑起来你会发现这个挂在AXI总线上的串口比PS自带的ttyPS0难伺候得多。明明ttyPS0刷日志刷得飞起UART Lite就是黑屏或者满屏乱码而且排查路径还不止一条——设备树时钟、内核驱动、终端工具配置、硬件连接每一环都可能出问题。这篇文章是我实际调试AXI UART Lite的完整记录从Petalinux侧的设备树与内核配置到minicom和screen这两种常见串口工具再到用devmem直接操作寄存器做硬件验证三条路都会给出具体命令和实测结果。如果你正卡在“minicom打开一片黑”或“echo出来的全是乱码”这个阶段按照这篇里的顺序排查大概率能找到问题所在。1. 先从硬件特性说起AXI UART Lite调试难的根源1.1 这个IP到底“简单”在哪又“坑”在哪AXI UART Lite在Xilinx IP家族里属于极简派。它不像PS端集成的Cadence UART那样带一堆调制解调器控制信号、自动流控和深度可配置的FIFO它能做的事只有两件往TX FIFO里写字节、从RX FIFO里读字节。寄存器一共四个RX FIFO、TX FIFO、状态寄存器、控制寄存器。单看这个设计确实比16550好搞定得多。但恰恰是这个“简单”让很多人都掉进了同一个坑以为它和ttyPS0一样上电就有、开个minicom就能用。实际上它挂在PL侧依赖bitstream加载依赖AXI总线和PS侧互连更依赖一个完整无误的设备树节点把地址、中断、时钟信息告诉内核。调试链路从应用层一路沉到硬件层任何一层出错表象都是“串口不通”但排查方向却天差地别。1.2 四条排查链路拆解可以把一次UART Lite调试问题拆成四层层次常见故障表现检查手段应用层/dev/ttyULx 不存在或打不开ls /dev/ttyUL*、权限检查驱动层dmesg 无 probe 信息或报错dmesg、cat /proc/tty/drivers设备树层probe 失败时钟错误设备树 dtsi 检查硬件层TX/RX 引脚无电平变化devmem 读寄存器很多人在第一层应用层死磕反复换minicom、重启板子实际根因在第三层。这就是为什么我强烈建议先熟悉devmem操作后面你会看到这个“原始”方法怎么一锤定音。1.3 和 ttyPS0 的本质区别一个很重要的区别ttyPS0是PS硬核自带的UART控制器只要bootrom级别就开始工作根本不依赖PL配置。而AXI UART Lite的物理逻辑在PL里如果bitstream没加载、或者PL侧的时钟没起来Linux里即便设备树写对了硬件也不响应。调试时先确认两件事一是PL bitstream确实加载了看dmesg里的FPGA manager信息或U-Boot阶段确认二是Vivado Address Editor里的地址和设备树reg一致。我自己的经历里有一个典型例子板子启动后ttyUL0节点已经出现但是读写完全没反应用devmem读状态寄存器也一直是0x05两个FIFO都空最后查了很久才发现是FSBL打包时没有把bitstream合进BOOT.BINPL侧逻辑根本没跑起来。这种问题光在minicom里折腾是永远找不到答案的。2. 把它点亮Petalinux侧的内核配置与设备树写法2.1 内核驱动CONFIG_SERIAL_UARTLITEPetalinux默认可能没开Xilinx UART Lite驱动尤其是用Petalinux向导建工程时如果没把UART Lite设为console内核配置里大概率是空的。手动打开方法petalinux-config -c kernel Device Drivers - Character devices - Serial drivers [*] Xilinx UART lite serial port support对应Kconfig项就是CONFIG_SERIAL_UARTLITE。如果还想让内核启动日志打在这个串口上还需要开Console support并把consolettyUL0传给内核。一般调试不太建议这么做毕竟系统启动日志走ttyPS0更稳UART Lite单独作为应用串口就够了。设置完保存退出编译petalinux-build2.2 设备树自动生成的不一定够用Vivado导出的hdf经过Petalinux的device tree generator会自动生成AXI UART Lite的节点类似这样axi_uartlite_0: serial42c00000 { compatible xlnx,xps-uartlite-1.00.a; reg 0x42c00000 0x10000; clocks misc_clk_0; clock-names s_axi_aclk; current-speed 115200; port-number 0; };自动生成的节点有几个容易出问题的地方clocks引用的是misc_clk_0而不是PS端的FCLK_CLK0。如果UART Lite实际挂在FCLK_CLK0上但设备树引用了不存在的时钟节点驱动probe就会报“failed to get clock”。current-speed可能没生成或者和Vivado IP配置的波特率不一致。这个属性影响用户空间获取默认波特率虽然驱动本身不靠它给出硬件速率但终端工具打开设备时会用它作为初始值不一致就有概率出乱码。reg地址空间大小取决于Vivado分配0x1000、0x10000都有别照抄别人的板子。所以更稳的做法是在system-user.dtsi里显式覆盖/include/ system-conf.dtsi axi_uartlite_0 { clocks clkc 15; /* FCLK_CLK0 */ clock-names s_axi_aclk; current-speed 115200; status okay; };clkc 15对应Zynq-7000的FCLK_CLK0如果挂在FCLK_CLK1就改为16以此类推。如果拿不准直接去Vivado的Address Editor和Clock Configurator里确认这是最不容易出错的方式。2.3 编译、打包与SD卡启动设备树改完后petalinux-build petalinux-package --boot --fsbl --fpga --u-boot --force第二条命令会从images/linux目录找到FSBL、bitstream和U-Boot生成BOOT.BIN。image.ub已经在编译时生成。如果你的U-Boot环境需要boot.scr脚本方式加载镜像可以把boot.cmd用mkimage封装mkimage -T script -C none -n Boot Script -d boot.cmd boot.scrboot.cmd内容最基本的就两行fatload mmc 0:1 0x2000000 image.ub bootm 0x2000000SD卡分区建议第一分区FAT32放BOOT.BIN、boot.scr、image.ub第二分区ext4放rootfs。上电后进入系统先验证节点dmesg | grep -i uartlite ls -l /dev/ttyUL*正常会看到uartlite: ttyUL0 at MMIO 0x42c00000 (irq 45) is a uartlite如果这一行都没有优先回头看驱动配置和设备树而不是去折腾minicom。2.4 驱动加载失败的典型报错uartlite: probe of 42c00000.serial failed with error -22绝大多数是时钟问题。检查设备树clocks的phandle和clock-names。clk: failed to enable 42c00000.serial同样指向时钟确认FCLK_CLK0确实使能且频率和Vivado一致。没有任何输出但地址对检查bitstream有没有加载以及/proc/device-tree里节点是否真的存在。提示在内核menuconfig里按“/”搜索UARTLITE可以快速定位到对应的配置项不用一层层翻菜单。3. 方法一minicom交互调试与乱码根因排查3.1 minicom的配置流程与常用参数minicom是Linux下最经典的串口终端工具Petalinux的rootfs里如果没装可以用petalinux-config -c rootfs勾选或者直接在开发机上用。首次使用推荐走一遍配置minicom -s进入配置菜单后选Serial port setup重点改四个参数按A把串口设备改成/dev/ttyUL0注意不是ttyPS0按E改成115200 8N1和Vivado IP配置的波特率一致按F硬件流控选NoUART Lite没有流控引脚开着必出问题按G软件流控一般也关掉保存为默认配置后直接运行minicom即可。快速验证链路是否通最简单的就是回环在minicom里敲几个字符看另一端或短接回环是否能收到。如果没有任何反应先别急着改minicom参数按下面顺序排查ls -l /dev/ttyUL0 cat /proc/tty/drivers | grep uartlite dmesg | grep -i uartlite设备节点不存在就回到第二章查驱动节点存在但minicom打不开就是权限问题把自己加进dialout组或者用sudo。3.2 minicom乱码从现象到根因的四步排查minicom乱码应该是UART Lite调试里最热门的问题了。我的排查顺序是固定的第一步排除工具配置。先确认minicom里波特率、流控设置和硬件一致。这里有个隐蔽的点AXI UART Lite的波特率是在Vivado IP配置时固化的Linux驱动没有波特率分频寄存器可以改所以应用层设的波特率必须和IP配置完全一致。如果IP配的是9600终端里设115200基本必乱。第二步排除硬件时钟。把设备树clocks写明确保和实际FCLK频率一致。这一步的坑在于即使时钟信息不对驱动也可能正常probe但UART Lite内部波特率生成依赖时钟时钟频率偏差会导致实际波特率偏移表现出来就是乱码。最直接的验证是用devmem去读状态寄存器如果RX FIFO一直在进数据说明硬件确实在收问题多半在波特率或终端配置。第三步短接回环测试。把板子上UART Lite的TX和RX引脚短接然后在minicom里输入一个字符。正常情况下字符会立即回显实际上就是直接从TX到RX绕过了外部设备。如果回显正常说明IP内部收发链路和驱动都OK乱码只可能出在外部接线、电平转换或另一端设备的参数上。这一步非常干净利落。第四步换工具交叉验证。如果minicom乱码立刻用screen或echo/cat方式发一个已知字符再用另一个终端读回来。这样可以区分“minicom自身问题”和“设备链路问题”。举个实际案例。有个朋友调一块板子minicom打开后全是乱码改了波特率也没用。我让他先跑一句devmem读状态寄存器。正常情况下空闲状态应该读到0x05TX FIFO空 RX FIFO空但他返回0x45多了一个RX underrun标志bit6。虽然underrun未必直接造成乱码但这个异常状态说明时钟配置有问题。回查设备树clocks写错了设备树里引用了一个不存在的时钟节点。修正之后STAT_REG稳定在0x05minicom输出恢复正常。这个案例说明乱码的根因常常不在minicom本身。3.3 minicom的日志与批量发送调试时经常需要保留串口会话记录。minicom在运行中可以用CtrlA然后按L开启日志日志会持续写入文件这对复现问题很有用。批量发送测试数据时minicom的CtrlA再按S可以调出上传菜单但板端一般没有rz代理更常见的方式是直接在终端里echo -n hello uartlite /dev/ttyUL0这句虽然不属于minicom但作为快速验证链路非常实用和minicom配合起来效率很高。4. 方法二screen轻量连接零配置快速看串口4.1 screen一条命令接管串口screen是Linux自带的终端复用器大部分人拿它管理远程会话但它也能直接接管串口。连接AXI UART Lite只需要一条命令screen /dev/ttyUL0 115200没有菜单、没有配置文件启动即连接。要断开时先按CtrlA再按K最后按Y确认退出。如果你开了多个screen会话screen -ls可以查看screen -r 会话ID可以恢复。4.2 滚屏与日志它不只是“能连”screen连接串口后被很多人吐槽“没法看历史输出”其实是可以的按CtrlA再按Esc进入Copy Mode方向键就能上下翻历史滚动按q退出。记录日志用CtrlA再按H会在当前目录生成screenlog.0并持续写入。还有一个容易忽略的点screen的滚屏缓冲区大小默认是1000行左右如果你需要更长的历史可以在启动前用defscrollback 5000这个命令设置写在~/.screenrc里即可。4.3 什么时候用screen而不是minicom我的习惯是这样如果只是快速看一眼UART Lite有没有输出、顺手发个测试字符screen是最快的打开即用退出也干净。但如果是较长时间的调试、需要反复切换参数、保存多组配置minicom更靠谱因为screen每次都要手动输设备名和波特率连错一个参数很难一眼看出来其实命令行里也能看到但不如minicom菜单直观。还有一个坑screen进程如果不退出就一直占着/dev/ttyUL0另一个程序再去打开就会报device busy。这时候要么用screen -d -r把会话拉回来要么直接kill掉对应screen进程否则你会白排查半天。我现在凡是遇到“设备被占用”的报错第一反应就是查是不是有残留的screen或者minicom进程。5. 方法三devmem直接寄存器操作不依赖驱动的硬件体检5.1 devmem是什么为什么调试时它最管用devmem是busybox里一个极简单的工具直接在用户态通过mmap访问物理内存对应的寄存器。在Petalinux文件系统里基本都内置。它最大的价值在于绕过一切驱动和上层协议直接把寄存器的值和状态摆在你面前。当/ttyUL0设备节点消失、minicom报“No such device”、dmesg里驱动报错时devmem是唯一还能验证“这颗IP本身是不是好的”的手段。使用格式很简单devmem ADDRESS [WIDTH [VALUE]]不写VALUE就是读写上VALUE就是写。WIDTH可以是8、16、32。对AXI UART Lite数据寄存器建议用8位宽度UART Lite数据寄存器低8位有效读状态用32位更直观。注意Petalinux的rootfs一般带busybox因此通常也有devmem命令。如果发现没有用busybox devmem或者从开发环境拷贝一个静态编译版本即可。另外如果驱动已经加载并且ttyUL0被终端工具占用devmem再去操作同一地址会和驱动打架建议先关掉minicom等进程。5.2 寄存器速查PG142里最常用的几个位把PG142的寄存器表浓缩成下面这张调试时基本只看这几个偏移名称方向用途0x00RX FIFO只读读一个接收字节低8位有效0x04TX FIFO只写写一个发送字节低8位有效0x08STAT_REG只读状态寄存器0x0CCTRL_REG只写控制寄存器STAT_REG里调试时最关心的是bit0、bit1、bit2、bit3bit名称读到时为1的含义0TX FIFO Empty可以发数据1TX FIFO FullFIFO满了不能写2RX FIFO Empty没有收到数据3RX FIFO FullFIFO满了要尽快读CTRL_REG初始化时最低限度要置位bit2TX Enable和bit3RX Enable也就是写入0x0C。5.3 实操初始化、发一个字节、读回状态假设基地址0x42C00000以Vivado Address Editor为准完整流程# 1. 使能TX和RX devmem 0x42C0000C 8 0x0C # 2. 查询状态看看是否就绪 devmem 0x42C00008 # 3. 发送字符 A (0x41) devmem 0x42C00004 8 0x41发送后如果对端串口工具收到A说明从寄存器到引脚整条链路都是通的。想发一串字符可以写一个shell循环for ch in 48 65 6c 6c 6f; do devmem 0x42C00004 8 $ch done接收方向就轮询STAT_REG的bit2为0说明RX FIFO里有数据while [ $(( $(devmem 0x42C00008) 0x4 )) -eq 0 ]; do :; done devmem 0x42C00000这种轮询方式在CPU占用率上比较粗糙但作为验证已经够用。如果要做更高效的收发测试建议写个小的C程序直接mmap不过那就是另一个话题了。5.4 用回环测试确认硬件最实用的一次体检是这样把板子上UART Lite的TX引脚和RX引脚直接用杜邦线短接然后# 清空状态 devmem 0x42C00008 8 0x30 # 发送 0x41 devmem 0x42C00004 8 0x41 # 回读状态bit2应变为0说明有数据进来 devmem 0x42C00008 # 读取RX FIFO应该返回0x41 devmem 0x42C00000如果读回的不是0x41要么寄存器地址、基地址不对要么IP未工作。这一步会把“硬件问题”和“软件问题”彻底分开回环不行大概率是Vivado配置或bitstream没加载回环正确那就往上修设备树或驱动。这里有个细节值得多说一句写STAT_REG清状态时写0x30bit4和bit5是清TX/RX overrun的标准写法如果你同时把bit6也置1会把RX underrun标志一起清掉。可以直接写0x70一次性清干净但是只清overrun也足够后续判断了。5.5 U-Boot阶段的寄存器验证比devmem更早的验证窗口在U-Boot阶段。Petalinux生成的BOOT.BIN在加载kernel前会有U-Boot命令行此时可以用md和mw直接操作相同的物理地址mw.l 0x42C00004 0x41 1 md.l 0x42C00008 1如果U-Boot阶段发送数据后逻辑分析仪能看到TX引脚波形那PL、时钟、引脚都确定是好的问题范围立刻缩小到内核侧。这个技巧在调试启动阶段特别有用很多人不知道U-Boot下也能这么干白在Linux里折腾半天。6. 三种方法实测对比什么时候该用哪一个6.1 同一块板子上的横向对比拿一块Zynq-7020板子AXI UART Lite挂在FCLK_CLK0上地址0x42C00000IP配置115200 8N1。三种方法的实测体验如下表场景minicomscreendevmem驱动正常日常收发稳定功能多可存日志简单直接启动快能收发但只适合验证驱动加载失败无ttyUL0不可用不可用可读寄存器判断硬件和时钟出现乱码能看到乱码但不知根因同样读STAT_REG看RX FIFO是否有数据快速区分终端配置问题和硬件问题需要自动化脚本测试不够方便不够方便配合shell脚本很方便看历史滚屏关联缓冲一般CtrlA Esc比较方便不支持6.2 我的建议顺序实际项目里我一般按这种方式组合先解决“通不通”再解决“好不好用”。驱动和设备树阶段优先用devmem做硬件体检确认IP和时钟正常应用层联调时用minicom做主力工具因为它的配置、日志、上传下载都是全的需要快速穿插验证或者临时看一眼输出时用screen顶上也够。如果你现在正遇到“minicom打开一片黑”或者“输出乱码”我的建议是别急着在minicom里反复改参数按这个顺序走一遍devmem读STAT_REG确认硬件和时钟 → 确认IP波特率 → 配置minicom流控和波特率 → 短接回环测试。绝大多数问题都能在这一串操作里定位。最后分享一个我自己吃亏换来的经验UART Lite的调试最忌讳的就是在应用层软件里反复试却不去确认硬件和时钟。自从我把devmem操作当成“调试第一板斧”之后UART Lite相关的问题排查速度快了很多。另外强烈建议项目一开始就把Vivado里的IP配置、地址分配、时钟频率这三组信息记录在板卡设计文档里谁调试、谁接手都能少走很多弯路。要是你手头也有一块调不通的UART Lite板子试试按文章里的顺序走一遍有结论了欢迎回来交流。