欢迎光临
我们一直在努力

ILA 在线调试:Vivado 抓取片内信号与定位下板问题(附完整实操)

本文是《FPGA入门到实战》专栏第11篇。上一篇介绍了建立时间、保持时间和 XDC 约束,本篇进入板级调试:当仿真结果正确,但程序下载到 FPGA 后仍然运行异常时,如何使用 Vivado ILA 观察芯片内部信号、设置触发条件并定位问题。读完本篇,你将能独立完成“添加调试信号 → 生成比特流 → 连接硬件 → 触发采样 → 分析波形”的完整流程。


1. 为什么需要 ILA

FPGA 中的大多数信号都位于芯片内部,既没有连接到 LED,也没有连接到示波器探针。仿真阶段可以观察任意信号,但实际下板后,真实时钟、外设时序、跨时钟域和板级连接都会引入新的问题。

常见现象包括:

  • 仿真波形正确,下载后 LED 或外设没有反应
  • UART、SPI 等接口偶发丢数,但无法稳定复现
  • 状态机进入异常状态,不知道由哪个输入触发
  • FIFO 出现 full 或 empty 异常
  • 跨时钟域信号偶发错误,普通功能仿真无法暴露
  • 计数器运行了,但输出值与预期不一致

传统方法是把内部信号临时连接到 IO,再用示波器或逻辑分析仪观察。这种方法会占用引脚,而且能观察的信号数量很少。ILA 可以直接采样 FPGA 内部信号,并通过 JTAG 将采样数据传回 Vivado。

ILA 的完整名称是 Integrated Logic Analyzer,即集成逻辑分析仪。可以把它理解成放在 FPGA 芯片内部的逻辑分析仪:

用户逻辑 ──→ ILA 采样存储 ──→ JTAG ──→ Vivado Hardware Manager

触发条件与采样时钟

ILA 主要由三部分组成:

  • 采样时钟:决定何时采集 Probe 信号
  • Probe 探针:连接需要观察的内部信号
  • 触发逻辑与采样存储器:满足条件时保存触发点前后的波形

  • 2. ILA 与仿真、示波器的区别

    调试方式观察对象优点局限
    行为仿真 RTL 信号 信号全部可见、可重复、定位方便 不能反映真实板级环境
    ILA FPGA 内部数字信号 不占用普通 IO,可观察状态机、计数器和总线 会消耗 BRAM/LUT,只能在采样时钟边沿观察
    示波器 外部模拟/数字波形 能观察电压、上升时间、抖动和信号完整性 难以观察芯片内部状态
    外部逻辑分析仪 板级数字接口 适合观察 SPI、UART 等外部总线 需要引出测试点,通道数量有限

    工程中通常不是四选一,而是组合使用:

    • 先通过仿真验证 RTL 功能
    • 下板后用 ILA 检查内部数据流和控制状态
    • 若怀疑电气问题,再使用示波器观察外部波形

    ILA 只能看到采样时钟边沿捕获到的数字值,不能替代示波器分析电压幅度、边沿质量和模拟抖动。


    3. 本篇实操目标

    下面使用一个带故障注入输入的计数器作为示例。模块正常工作时不断计数;当 fault_in 有效时,计数器错误地清零。我们要使用 ILA 捕获清零瞬间,并观察触发前后的 counter、enable 和 fault_in。

    3.1 示例模块

    module ila_counter_demo (
    input wire clk , // 系统时钟
    input wire rstn , // 低有效复位
    input wire enable , // 计数使能
    input wire fault_in, // 故障注入
    output wire [31:0] count // 当前计数值
    );

    (* MARK_DEBUG = "TRUE" *) reg [31:0] counter;
    (* MARK_DEBUG = "TRUE" *) wire enable_debug;
    (* MARK_DEBUG = "TRUE" *) wire fault_debug;

    assign enable_debug = enable;
    assign fault_debug = fault_in;
    assign count = counter;

    always @(posedge clk or negedge rstn)
    begin
    if (rstn == 1'b0)
    begin
    counter <= 32'd0;
    end
    else if (fault_in == 1'b1)
    begin
    counter <= 32'd0;
    end
    else if (enable == 1'b1)
    begin
    counter <= counter + 32'd1;
    end
    else
    begin
    counter <= counter;
    end
    end

    endmodule

    代码中的 MARK_DEBUG 属性告诉 Vivado:综合时保留这些网络,并把它们作为候选调试信号。属性不会改变原有逻辑功能,但会影响优化结果和资源占用,因此只应标记真正需要观察的信号。

    3.2 为什么不直接观察所有信号

    ILA 并不是“信号越多越好”。每增加一个 Probe 位宽、每提高一档采样深度,都会增加片上存储资源消耗。

    例如观察以下信号:

    counter 32 bit
    state 4 bit
    data_valid 1 bit
    data_ready 1 bit
    总位宽 38 bit

    若采样深度为 4096,至少需要保存 38 × 4096 位数据,还要加上触发和控制逻辑。大型工程中若一次标记数百个信号,很容易明显增加 BRAM 占用并影响布局布线。

    正确做法是围绕问题建立“最小观察集合”:

    • 一个表示数据本身的信号
    • 一个或两个握手/使能信号
    • 当前状态或错误标志
    • 能够精确复现问题的触发信号

    4. 添加 ILA 的三种方法

    Vivado 中常用的 ILA 添加方式有三种。

    4.1 使用 MARK_DEBUG 属性

    直接在 RTL 中标记信号:

    (* MARK_DEBUG = "TRUE" *) wire [7:0] rx_data;
    (* MARK_DEBUG = "TRUE" *) wire rx_valid;

    完成综合后,打开综合设计并执行 Set Up Debug,Vivado 会列出这些信号,然后自动插入调试核。

    这种方式的优点:

    • 调试信号与源码对应关系清晰
    • 重新综合后不容易丢失
    • 适合长期保留的关键状态信号

    需要注意:不要在不需要调试时长期保留大量 MARK_DEBUG,否则可能限制综合器优化。

    4.2 在综合后设计中选择信号

    不修改 RTL,执行以下步骤:

  • 运行综合
  • 点击 Open Synthesized Design
  • 在 Netlist 或 Schematic 中找到目标网络
  • 右键选择 Mark Debug
  • 点击 Set Up Debug 完成探针配置
  • 这种方式适合临时排查问题,但工程重新综合或网络名称改变后,需要重新确认探针。

    4.3 手动例化 ILA IP 核

    在 IP Catalog 中搜索 ILA,配置探针数量、位宽和采样深度,然后像普通模块一样例化:

    ila_0 u_ila (
    .clk (clk ),
    .probe0 (counter ),
    .probe1 (enable ),
    .probe2 (fault_in )
    );

    手动例化的优势是接口固定、行为明确,适合调试结构长期保留在工程中的情况;缺点是每次改变探针位宽都可能需要重新定制 IP。

    对于初学者,本篇推荐使用 MARK_DEBUG + Set Up Debug,操作直观且不需要手动管理 ILA 端口。


    5. 使用 Set Up Debug 插入 ILA

    5.1 运行综合

    点击左侧 Flow Navigator 中的:

    SYNTHESIS → Run Synthesis → Open Synthesized Design

    综合完成后,在 Netlist 窗口中可以看到 counter、enable_debug 和 fault_debug。如果源码已添加 MARK_DEBUG,这些信号通常会带有调试标记。

    5.2 打开 Set Up Debug

    在综合设计界面点击:

    Tools → Set Up Debug

    向导会显示候选网络。确认需要观察的信号已经加入 Probe 列表:

    Probe位宽用途
    counter[31:0] 32 观察计数值
    enable_debug 1 判断计数是否允许
    fault_debug 1 作为故障触发条件

    5.3 选择采样时钟

    ILA 的采样时钟必须满足以下要求:

    • 时钟稳定且持续运行
    • 时钟频率位于器件和设计允许范围内
    • 被观察信号最好与采样时钟处于同一时钟域
    • 触发前采样时钟已经启动

    本例所有信号都在 clk 时钟域,因此选择 clk 作为 ILA 时钟。

    如果用 A 时钟采样 B 时钟域信号,波形可能漏掉窄脉冲,也可能在时钟域交界处采到不稳定值。多时钟域工程通常需要为不同域分别设置 ILA,或先通过 CDC 电路把关键事件同步到采样域。

    5.4 设置采样深度

    常见采样深度包括 1024、2048、4096、8192 等。深度越大,可观察的时间窗口越长,但会消耗更多 BRAM。

    假设采样时钟是 100 MHz:

    采样深度可观察总时间
    1024 10.24 μs
    4096 40.96 μs
    16384 163.84 μs

    首次调试建议从 1024 或 4096 开始。如果目标事件间隔很长,不应盲目把深度调到最大,而应设计一个事件标志作为触发条件,只捕获问题附近的波形。

    完成向导后保存约束,Vivado 会插入 ILA 调试核。随后执行:

    Run Implementation → Generate Bitstream


    6. 连接开发板并下载

    6.1 打开 Hardware Manager

    比特流生成完成后点击:

    Open Hardware Manager → Open Target → Auto Connect

    连接成功后,Hardware 窗口会显示 JTAG 链中的 FPGA 器件。

    6.2 下载 BIT 与 LTX 文件

    包含 ILA 的工程通常会生成两类关键文件:

    • .bit:FPGA 配置数据
    • .ltx:调试探针与内部网络的映射信息

    正常情况下 Vivado 会自动关联两者。如果下载后找不到 ILA,重点检查:

  • 当前 .bit 是否由包含 ILA 的最新工程生成
  • .ltx 是否与 .bit 同一次生成
  • 是否误用了旧目录中的比特流
  • ILA 的采样时钟是否正在运行
  • 选择器件并点击 Program Device 完成下载。下载后 Hardware 窗口中应出现类似 hw_ila_1 的调试核。


    7. 设置触发条件并采集波形

    7.1 立即触发

    第一次连接 ILA 时,可以先不设置条件,点击 Run Trigger Immediate 立即采样。这样可以确认:

    • ILA 核工作正常
    • 采样时钟正在运行
    • Probe 信号不是全部固定为 X 或常量
    • counter 在持续变化

    7.2 条件触发

    为了捕获故障发生瞬间,把 fault_debug 设置为:

    fault_debug == 1

    然后点击 Run Trigger。ILA 进入等待状态,当 fault_in 变为高电平时触发并返回波形。

    波形中应看到:

  • 触发前 counter 持续递增
  • fault_debug 在触发点变为 1
  • 下一个有效时钟沿后 counter 清零
  • fault_debug 恢复后,若 enable_debug 为 1,计数器重新计数
  • 7.3 调整触发位置

    触发位置决定采样缓存中“触发前”和“触发后”各保留多少数据。

    • 触发点放在中间:同时观察原因和结果,最常用
    • 触发点靠后:保留更多触发前历史,适合追溯错误来源
    • 触发点靠前:保留更多触发后波形,适合观察后续恢复过程

    如果只看到故障结果,却看不到故障产生前的状态,应把触发位置向后移动,增加触发前采样比例。

    7.4 总线显示格式

    对于计数器、地址、数据等多位信号,可以在波形窗口中修改显示进制:

    • Binary:适合观察单 bit 和状态标志
    • Hexadecimal:适合观察总线和寄存器
    • Unsigned Decimal:适合观察计数值
    • Signed Decimal:适合观察补码数据

    同一个 32'hFFFF_FFFF,按无符号显示是 4294967295,按有符号显示是 -1。分析算法数据时必须选择正确的显示格式。


    8. 用 ILA 定位常见问题

    8.1 状态机卡死

    建议观察:

    • current_state
    • 关键输入条件
    • 超时计数器
    • 握手信号 valid/ready

    触发条件可以设置为非法状态,或设置为某个状态持续时间超过阈值后产生的 timeout 标志。

    不要只观察 current_state。如果没有同时观察状态转移条件,只能知道“卡在哪里”,无法知道“为什么没有离开”。

    8.2 UART 接收错误

    建议观察:

    • 同步后的 uart_rx
    • 波特率采样使能
    • 位计数器
    • 接收移位寄存器
    • rx_valid

    触发条件可以设为 rx_valid == 1,或者在检测到起始位后触发。若采样深度有限,应优先捕获一帧完整数据,而不是长期无条件采样。

    8.3 FIFO 溢出或读空

    建议观察:

    • wr_en、rd_en
    • full、empty
    • 数据计数值
    • 上游 valid/ready

    典型触发条件:

    (wr_en == 1) && (full == 1)

    或者:

    (rd_en == 1) && (empty == 1)

    这样可以直接捕获违规读写发生前后的数据流状态。

    8.4 AXI-Stream 数据停滞

    AXI-Stream 发生一次有效传输的条件是:

    tvalid == 1 && tready == 1

    若数据链路停滞,需要同时观察 tvalid、tready、tdata 和上游/下游状态机。只看 tdata 是否变化无法判断数据是否真正完成传输。


    9. VIO:在 Vivado 中控制内部信号

    ILA 用于观察,VIO(Virtual Input/Output)用于在 Hardware Manager 中读写内部信号。

    典型用途:

    • 手动产生模块启动脉冲
    • 在线修改测试模式
    • 控制内部复位
    • 修改测试数据或阈值
    • 读取少量状态信号

    例如将 VIO 输出连接到测试使能:

    wire test_enable;
    wire clear_error;

    vio_0 u_vio (
    .clk (clk ),
    .probe_in0 (error_flag ),
    .probe_out0 (test_enable),
    .probe_out1 (clear_error)
    );

    VIO 非常适合调试,但不要让调试核成为正式产品运行所必需的控制接口。量产设计应由寄存器、外部接口或软件驱动完成正常控制。


    10. 常见问题与排查方法

    10.1 下载后看不到 ILA

    依次检查:

  • 是否重新运行了综合、实现和比特流生成
  • 下载的是否为最新 .bit
  • .ltx 是否与 .bit 匹配
  • Set Up Debug 是否真正保存并写入工程
  • JTAG 是否连接到正确器件
  • 10.2 ILA 一直等待,无法触发

    可能原因:

    • 触发条件根本不会发生
    • 探针位宽或进制设置错误
    • 触发信号位于其他时钟域,窄脉冲被漏采
    • 被测模块仍处于复位状态
    • 采样时钟没有运行

    先使用立即触发确认时钟和探针正常,再逐步增加触发条件。不要一开始就设置多个复杂条件。

    10.3 波形全部为 0

    重点检查:

    • 模块时钟是否正常
    • 复位是否一直有效
    • Probe 是否连接到综合后的正确网络
    • 输入信号是否真的进入 FPGA
    • 管脚约束和 IO 电平标准是否正确

    10.4 加入 ILA 后时序变差

    ILA 会增加信号扇出、布线压力和 BRAM 使用。处理方法:

    • 减少 Probe 数量和位宽
    • 降低采样深度
    • 不要跨很远区域抓取大量信号
    • 为不同模块分别建立小型 ILA
    • 调试结束后移除不需要的调试核
    • 重新检查 Timing Summary,不能因为“只是调试”就忽略时序违规

    10.5 重新综合后探针消失

    综合器可能优化、合并或重命名内部网络。对必须稳定保留的信号使用 MARK_DEBUG;对组合表达式可先增加一个有明确名称的中间信号,再标记该信号。


    11. 工程调试建议

    11.1 先提出问题,再选择信号

    错误方式:看到什么信号就全部加入 ILA。

    正确方式:先写出需要验证的判断,例如:

    UART 丢数是否由 FIFO 满导致?

    围绕这个问题,只观察 UART 接收有效、FIFO 写使能、FIFO 满标志和数据计数值。观察集合越聚焦,波形越容易解释。

    11.2 给异常建立专用触发标志

    若异常条件很复杂,可以在 RTL 中生成一个单 bit 调试标志:

    wire fifo_write_error;

    assign fifo_write_error = fifo_wr_en & fifo_full;

    ILA 直接对 fifo_write_error == 1 触发,比在界面中配置多个条件更清晰,也方便后续复用。

    11.3 记录调试配置

    建议在调试记录中写清楚:

    • 使用的比特流时间和 Git 提交
    • Probe 列表与位宽
    • 采样时钟和采样深度
    • 触发条件与触发位置
    • 复现步骤
    • 波形结论

    否则几天后即使再次捕获到相同波形,也可能无法确认它对应哪一版逻辑。

    11.4 调试完成后重新验证

    移除 ILA 可能改变布局布线结果。正式交付前应:

  • 移除不需要的调试信号
  • 重新综合和实现
  • 确认时序收敛
  • 重新执行板级功能测试

  • 12. 本篇总结

    本篇完成了 Vivado ILA 在线调试的完整流程:

  • 通过 MARK_DEBUG 或手动例化选择内部信号
  • 在综合后设计中使用 Set Up Debug 插入 ILA
  • 选择稳定的采样时钟并合理设置采样深度
  • 生成并下载匹配的 .bit 与 .ltx 文件
  • 使用立即触发验证链路,再使用条件触发捕获异常
  • 根据状态、数据和握手信号分析问题原因
  • 调试完成后移除多余探针并重新进行时序验证
  • ILA 最重要的能力不是“看到更多信号”,而是在正确的时刻捕获与问题有关的最小信号集合。当你能够根据故障现象设计触发条件时,FPGA 板级调试就不再是反复猜测和重新烧录,而会变成一套可以验证、可以复现的工程流程。

    下一篇将进入按键消抖实战,从机械按键的物理抖动开始,完成同步、滤波、边沿检测与仿真验证。


    参考资料

    • AMD Vivado Design Suite User Guide: Programming and Debugging(UG908)
    • AMD Vivado Design Suite User Guide: Using Constraints(UG903)
    赞(0)
    未经允许不得转载:171主机测评 » ILA 在线调试:Vivado 抓取片内信号与定位下板问题(附完整实操)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址