🔍 第31课:逻辑分析仪

多通道采样 + 触发字匹配 + 协议解码 — 调试数字信号!

🏆 成就:信号侦探 ✅ Verilator验证通过

🔍
信号侦探
16通道逻辑分析 + 触发字 + 协议解码

🔍 逻辑分析仪原理

逻辑分析仪是数字调试的终极武器——同时采样多路数字信号,找到触发条件后捕获波形,用协议解码还原通信内容。FPGA本身就是最棒的逻辑分析仪!

逻辑分析仪架构 被测信号[15:0] ──→ [采样] ──→ 环形缓冲(1024×16) │ [触发匹配] trig_word & trig_mask == probe_in & trig_mask │ 触发后继续采样256点 │ [VGA波形显示] 16通道时序图 + 协议解码 触发条件示例: ● UART起始位: ch0=0, mask=0x0001 ● SPI片选: ch3=0, mask=0x0008 ● 任意通道变化: mask=0xFFFF 协议解码: ● UART: 找起始位→采8位→找停止位 ● SPI: CS↓→逐位读MOSI/MISO ● I2C: START→地址→ACK→数据

📐 逻辑分析仪核心

Veriloglogic_analyzer.v — 16通道逻辑分析仪
// 逻辑分析仪 - 16通道采样+触发
module logic_analyzer #(
    parameter CH_W = 16,          // 通道数
    parameter DEPTH = 1024,       // 采样深度
    parameter TRIG_W = 16         // 触发字位宽
)(
    input  wire              clk,
    input  wire              rst,
    // 被测信号输入
    input  wire [CH_W-1:0]   probe_in,
    // 控制
    input  wire              arm,          // 1=开始采样
    input  wire [TRIG_W-1:0] trig_word,   // 触发字
    input  wire [TRIG_W-1:0] trig_mask,   // 触发掩码(1=有效)
    input  wire              trig_en,      // 触发使能
    output reg               armed,        // 已武装
    output reg               triggered,    // 已触发
    output reg               done,         // 采样完成
    // 采样数据读出
    input  wire [9:0]        rd_addr,
    output wire [CH_W-1:0]   rd_data
);
    // 采样缓冲: 1024×16bit BRAM
    reg [CH_W-1:0] sample_mem [0:DEPTH-1];
    reg [9:0] wr_ptr;
    reg [9:0] post_trig_cnt;

    localparam POST_TRIG = 256; // 触发后采样256点

    // 采样状态机
    localparam IDLE=0, WAIT_TRIG=1, CAPTURE=2, CAPTURED=3;
    reg [1:0] state;

    // 触发匹配
    wire match = trig_en &&
                  ((probe_in & trig_mask) == (trig_word & trig_mask));

    always @(posedge clk) begin
        if (rst) begin
            state <= IDLE; wr_ptr <= 0;
            armed <= 0; triggered <= 0; done <= 0;
            post_trig_cnt <= 0;
        end else begin
            case(state)
                IDLE: begin
                    done <= 0; triggered <= 0;
                    if (arm) begin
                        armed <= 1;
                        wr_ptr <= 0;
                        state <= WAIT_TRIG;
                    end
                end
                WAIT_TRIG: begin
                    // 持续采样(环形缓冲)
                    sample_mem[wr_ptr] <= probe_in;
                    wr_ptr <= wr_ptr + 1;
                    // 触发检测
                    if (match) begin
                        triggered <= 1;
                        post_trig_cnt <= 0;
                        state <= CAPTURE;
                    end
                end
                CAPTURE: begin
                    sample_mem[wr_ptr] <= probe_in;
                    wr_ptr <= wr_ptr + 1;
                    post_trig_cnt <= post_trig_cnt + 1;
                    if (post_trig_cnt >= POST_TRIG) begin
                        done <= 1;
                        armed <= 0;
                        state <= CAPTURED;
                    end
                end
                CAPTURED: begin
                    // 等待数据读出
                    if (arm) begin
                        done <= 0; triggered <= 0;
                        wr_ptr <= 0;
                        armed <= 1;
                        state <= WAIT_TRIG;
                    end
                end
            endcase
        end
    end

    assign rd_data = sample_mem[rd_addr];

endmodule

🧪 逻辑分析仪测试

SystemVeriloglogic_analyzer_tb.sv — 逻辑分析仪测试台
module logic_analyzer_tb;
    logic clk=0, rst=1;
    logic [15:0] probe_in=0;
    logic arm=0;
    logic [15:0] trig_word=16'h0001;
    logic [15:0] trig_mask=16'h000F;
    logic trig_en=1;
    logic armed, triggered, done;
    logic [9:0] rd_addr=0;
    logic [15:0] rd_data;

    logic_analyzer uut(.*);
    always #10 clk = ~clk;

    initial begin
        rst=1; #50; rst=0;
        $display("--- 逻辑分析仪测试 ---");
        // 武装
        arm=1; #20; arm=0;
        $display("  armed=%b", armed);
        // 模拟信号变化
        probe_in=16'h0000; #100;
        probe_in=16'h0001; // 触发!
        #20;
        $display("  triggered=%b", triggered);
        // 等待采样完成
        wait(done);
        $display("  采样完成 ✓");
        // 读出数据
        for(int i=0; i<10; i=i+1) begin
            rd_addr=i; #10;
            $display("  [%0d]=0x%h", i, rd_data);
        end
        $display("逻辑分析仪测试完成 ✓");
        #100; $finish;
    end
endmodule

📊 触发模式对比

触发模式条件用途
简单匹配信号=触发字基本触发
掩码匹配(信号&掩码)=触发字忽略无关位
边沿触发信号上升/下降沿检测变化
序列触发A→B→C按序出现复杂协议
超时触发信号N周期未变检测挂死

💡 环形缓冲:触发前的数据也不能丢!用环形缓冲——持续写入,地址自动回绕。触发后继续写POST_TRIG个点再停止。这样触发点前后都有数据,能看到完整事件!

练习1:实现UART协议解码:采样数据→还原字节

练习2:添加VGA波形显示:16通道时序图

练习3:实现SPI协议自动解码

练习4:添加边沿触发模式(上升/下降沿)

练习5:用UART把采样数据发到PC用Python显示

🔍
信号侦探
完成本课练习,打造FPGA逻辑分析仪!

🔧 Verilator验证步骤

步骤1verilator --lint-only logic_analyzer.v

步骤2verilator --binary -j 0 logic_analyzer.v logic_analyzer_tb.sv

步骤3./obj_dir/Vlogic_analyzer_tb

🔬 协议解码实现

UART解码:找到起始位→按波特率采样8位→校验停止位→输出HEX

SPI解码:CS↓→每个SCLK上升沿读MOSI/MISO→每8位输出1字节

I2C解码:检测START→读7位地址+R/W→ACK→数据→STOP

PWM解码:测量高/低脉冲宽度→计算占空比和频率

💡 逻辑分析仪 vs 示波器

逻辑分析仪:多通道(8~64),只看0/1,协议解码,数字调试

示波器:2~4通道,模拟电压,波形细节,模拟调试

混合信号:MSO=示波器+逻辑分析仪,兼顾两者

FPGA优势:可以同时实现两者!第30课+第31课合体

🔍 逻辑分析仪深入:采样率选择

逻辑分析仪的采样率必须远高于被测信号频率,否则会漏掉毛刺和时序问题。

经验法则:采样率≥10倍信号频率,才能准确捕获边沿

UART:9600bps→采样率≥100kHz,115200→≥1.15MHz

SPI:1MHz SCLK→采样率≥10MHz

毛刺捕获:FPGA采样率=系统时钟50MHz→可捕获>20ns的毛刺

存储深度:50MHz×1ms=50K点,16通道=100KB BRAM

📊 逻辑分析仪资源需求

通道数采样深度BRAM需求LUT需求最大采样率
810241×18Kb~20050MHz
1610242×18Kb~40050MHz
1640968×18Kb~50050MHz
32409616×18Kb~90050MHz

🔬 Verilator仿真

1. lintverilator --lint-only logic_analyzer.v

2. 编译verilator --binary -j 0 --trace logic_analyzer.v logic_analyzer_tb.sv

3. 运行./obj_dir/Vlogic_analyzer_tb