多通道采样 + 触发字匹配 + 协议解码 — 调试数字信号!
🏆 成就:信号侦探 ✅ Verilator验证通过
逻辑分析仪是数字调试的终极武器——同时采样多路数字信号,找到触发条件后捕获波形,用协议解码还原通信内容。FPGA本身就是最棒的逻辑分析仪!
// 逻辑分析仪 - 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];
endmodulemodule 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显示
步骤1:verilator --lint-only logic_analyzer.v
步骤2:verilator --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解码:测量高/低脉冲宽度→计算占空比和频率
逻辑分析仪:多通道(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需求 | 最大采样率 |
|---|---|---|---|---|
| 8 | 1024 | 1×18Kb | ~200 | 50MHz |
| 16 | 1024 | 2×18Kb | ~400 | 50MHz |
| 16 | 4096 | 8×18Kb | ~500 | 50MHz |
| 32 | 4096 | 16×18Kb | ~900 | 50MHz |
1. lint:verilator --lint-only logic_analyzer.v
2. 编译:verilator --binary -j 0 --trace logic_analyzer.v logic_analyzer_tb.sv
3. 运行:./obj_dir/Vlogic_analyzer_tb