Quick Start
- 步骤1:准备硬件平台——选用国产FPGA开发板(例如紫光同创Logos-2系列或安路科技SF1系列),确保板载至少1路MIPI CSI摄像头接口、1路I2S麦克风阵列(或模拟麦克风+ADC)、1路UART/USB转串口用于调试日志。
- 步骤2:安装EDA工具——下载并安装紫光同创PDS(或安路科技TD)最新版本(示例:PDS 2025.2或TD 6.0),并申请免费License。
- 步骤3:获取大赛官方IP库——从大赛官网下载多模态传感器融合参考设计包(含MIPI D-PHY接收器IP、I2S音频采集IP、AXI4-Stream FIFO、双端口BRAM控制器)。
- 步骤4:创建工程并导入IP——打开PDS/TD,新建工程,选择目标器件(如Logos-2 L2UART-6BG256),将参考设计包中的IP核添加到工程。
- 步骤5:编写顶层RTL——实例化视频采集模块、音频采集模块、融合处理模块(如简单的加权平均或帧同步FIFO),将视频像素流与音频采样流合并为单一AXI4-Stream数据包。
- 步骤6:运行综合与实现——点击“Synthesis”和“Place & Route”,观察资源利用率(LUT/BRAM/DSP)不超过器件总资源的70%,时序满足约束(setup slack > 0)。
- 步骤7:生成比特流并下载——生成.bit文件,通过JTAG下载至开发板;连接串口终端(波特率115200),上电后应看到“Sensor Fusion Init OK”打印。
- 步骤8:验证融合输出——用逻辑分析仪(如PDS内置ChipWatcher)捕获AXI4-Stream输出,检查每个数据包头部是否包含视频行号+音频时间戳,数据内容无毛刺。
前置条件与环境
| 项目/推荐值 | 说明 | 替代方案 |
|---|---|---|
| FPGA器件 | 紫光同创 Logos-2 L2UART-6BG256 (示例) | 安路科技 SF1S60G, 高云 GW5AT-60 |
| EDA版本 | PDS 2025.2 (示例) | TD 6.0, Gowin Yun 1.9.10 |
| 仿真器 | ModelSim SE-64 2024.2 (或PDS内置Simulator) | Vivado Simulator (仅限Xilinx平台) |
| 时钟/复位 | 板载50MHz晶振,按键复位(低有效) | PLL倍频至100MHz/200MHz |
| 接口依赖 | MIPI CSI-2 (4-lane, 1Gbps/lane), I2S (标准Philips格式) | 并行摄像头+SPI ADC |
| 约束文件 | .sdc 时序约束(主时钟50MHz, 虚拟时钟200MHz for MIPI) | 使用EDA向导自动生成 |
| 调试工具 | ChipWatcher (PDS内置逻辑分析仪) | Saleae逻辑分析仪(外部) |
目标与验收标准
完成本设计后,应满足以下验收标准:
- 功能点:实时采集640×480@30fps视频流(YUV422格式)与16位/48kHz音频流(单声道),在FPGA内完成帧同步融合,输出AXI4-Stream数据包(每包包含1行视频像素+4个音频采样+时间戳)。
- 性能指标:视频帧率≥30fps(无丢帧),音频采样无断裂,融合延迟≤2个视频行时间(约66μs @ 30fps)。
- 资源/Fmax:LUT使用率≤40%,BRAM≤50%,DSP≤20%;系统时钟200MHz下Fmax≥180MHz(示例值,以实际综合报告为准)。
- 关键波形/日志:ChipWatcher捕获到AXI4-Stream tvalid与tready握手正常,数据包头部0xAA55标志正确;串口打印“Frame: xxx, Audio samples: yyy”每帧更新。
实施步骤
阶段1:工程结构与顶层模块
创建工程目录结构:
sensor_fusion/
├── rtl/
│ ├── top.v
│ ├── mipi_rx.v
│ ├── i2s_rx.v
│ ├── fusion_engine.v
│ └── axis_packer.v
├── ip/
│ ├── mipi_dphy_phy.xci
│ ├── i2s_controller.xci
│ └── axis_fifo.xci
├── sim/
│ ├── tb_top.v
│ └── testbench_script.tcl
├── constraints/
│ └── top.sdc
└── scripts/
└── build.tcl逐行说明
- 第1行:顶层目录,包含所有子目录。
- 第2-7行:RTL源码目录,包含顶层模块、MIPI接收、I2S接收、融合引擎、AXI4-Stream打包器。
- 第8-11行:IP核目录,存放MIPI D-PHY物理层、I2S控制器、AXI4-Stream FIFO的.xci配置文件。
- 第12-14行:仿真目录,包含testbench和运行脚本。
- 第15行:约束文件,定义时钟、复位、I/O时序。
- 第16行:自动化构建脚本,用于一键综合、实现、生成比特流。
阶段2:关键模块——MIPI视频接收
MIPI CSI-2接收模块负责将4-lane D-PHY串行数据转换为并行像素流。以下为核心RTL片段:
module mipi_rx (
input wire clk_200m, // 200MHz 参考时钟(来自PLL)
input wire rst_n, // 异步复位,低有效
input wire [3:0] dphy_data_p, // D-PHY 正极数据
input wire [3:0] dphy_data_n, // D-PHY 负极数据
input wire dphy_clk_p, // D-PHY 正极时钟
input wire dphy_clk_n, // D-PHY 负极时钟
output reg [7:0] pixel_data, // 8-bit 像素数据(YUV422)
output reg pixel_valid, // 像素有效标志
output reg vsync, // 帧同步(高有效)
output reg hsync // 行同步(高有效)
);
// 实例化 D-PHY IP 核(由IP目录导入)
wire [7:0] dphy_byte_data;
wire dphy_byte_valid;
mipi_dphy_phy u_dphy (
.refclk (clk_200m),
.rst_n (rst_n),
.data_p (dphy_data_p),
.data_n (dphy_data_n),
.clk_p (dphy_clk_p),
.clk_n (dphy_clk_n),
.byte_data (dphy_byte_data),
.byte_valid(dphy_byte_valid)
);
// CSI-2 协议解析(简化:仅处理RAW8格式)
reg [1:0] state;
localparam IDLE = 2'd0, HEADER = 2'd1, DATA = 2'd2;
always @(posedge clk_200m or negedge rst_n) begin
if (!rst_n) begin
state <= IDLE;
pixel_valid <= 1'b0;
vsync <= 1'b0;
hsync <= 1'b0;
end else begin
case (state)
IDLE: begin
if (dphy_byte_valid && dphy_byte_data == 8'h1E) begin // 帧起始码
state <= HEADER;
vsync <= 1'b1;
end
end
HEADER: begin
if (dphy_byte_valid && dphy_byte_data == 8'h2C) begin // 行起始码
state <= DATA;
hsync <= 1'b1;
end
end
DATA: begin
pixel_data <= dphy_byte_data;
pixel_valid <= dphy_byte_valid;
if (dphy_byte_valid && dphy_byte_data == 8'h3C) begin // 行结束码
state <= HEADER;
hsync <= 1'b0;
end
if (dphy_byte_valid && dphy_byte_data == 8'h1F) begin // 帧结束码
state <= IDLE;
vsync <= 1'b0;
end
end
default: state <= IDLE;
endcase
end
end
endmodule逐行说明
- 第1-13行:模块端口声明。clk_200m是MIPI D-PHY所需的参考时钟(通常由板载PLL生成);dphy_data_p/n是4对差分数据线;dphy_clk_p/n是差分时钟线;pixel_data等为输出像素流与控制信号。
- 第15-23行:实例化MIPI D-PHY IP核。该IP将串行差分信号转换为8位并行字节(byte_data)和有效标志(byte_valid)。
- 第25-27行:定义状态机状态与本地参数。IDLE等待帧起始,HEADER等待行起始,DATA传输像素数据。
- 第28-53行:状态机逻辑。在IDLE状态检测到帧起始码0x1E后进入HEADER;HEADER检测到行起始码0x2C后进入DATA;DATA中每个有效字节输出为像素,遇到行结束码0x3C回到HEADER,遇到帧结束码0x1F回到IDLE。
- 第50-52行:行结束与帧结束处理。注意:行结束码后立即返回HEADER准备下一行,帧结束码后回到IDLE等待下一帧。vsync与hsync信号用于后续融合模块的帧同步。
常见坑与排查(阶段2)
- 坑1:D-PHY IP未正确配置lane数——检查IP配置中lane数是否为4,且与传感器输出匹配。若传感器只输出2-lane,需修改IP并调整顶层连线。
- 坑2:时钟域交叉导致亚稳态——MIPI接收模块工作在200MHz域,而后续融合模块可能工作在100MHz域。务必在输出端添加异步FIFO(使用axis_fifo IP)进行跨时钟域同步。
阶段3:关键模块——I2S音频接收
I2S接收模块从麦克风阵列采集音频数据,输出16位采样值。核心RTL:
module i2s_rx (
input wire clk_12m, // 12.288MHz 音频位时钟(由PLL分频)
input wire rst_n,
input wire i2s_ws, // 字选择(左右声道,0=左,1=右)
input wire i2s_sd, // 串行数据
output reg [15:0] audio_sample, // 16位音频采样
output reg sample_valid // 采样有效(每个WS周期一次)
);
reg [4:0] bit_cnt;
reg [15:0] shift_reg;
reg ws_prev;
always @(posedge clk_12m or negedge rst_n) begin
if (!rst_n) begin
bit_cnt <= 5'd0;
shift_reg <= 16'd0;
ws_prev <= 1'b0;
audio_sample <= 16'd0;
sample_valid <= 1'b0;
end else begin
ws_prev <= i2s_ws;
// 检测WS边沿:上升沿表示新声道开始
if (i2s_ws && !ws_prev) begin
bit_cnt <= 5'd0;
end
// 在每个SCK下降沿采样数据(I2S标准)
// 这里假设clk_12m是SCK的2倍频,实际应使用SCK边沿
// 为简化,直接使用clk_12m上升沿(需确保时序匹配)
if (bit_cnt < 5'd16) begin
shift_reg <= {shift_reg[14:0], i2s_sd};
bit_cnt <= bit_cnt + 1'b1;
end
// 当bit_cnt达到16时,输出采样
if (bit_cnt == 5'd16) begin
audio_sample <= shift_reg;
sample_valid <= 1'b1;
end else begin
sample_valid <= 1'b0;
end
end
end
endmodule逐行说明
- 第1-7行:模块端口。clk_12m为音频位时钟(典型值12.288MHz),i2s_ws为字选择信号(频率=采样率=48kHz),i2s_sd为串行数据线。
- 第9-11行:内部寄存器。bit_cnt计数接收位数,shift_reg暂存串行数据,ws_prev用于检测WS边沿。
- 第13-33行:主逻辑。检测WS上升沿(左声道开始)时复位bit_cnt;在clk_12m上升沿移位输入数据;当bit_cnt达到16时,输出完整的16位采样并置位sample_valid。
- 注意:实际I2S协议要求数据在SCK下降沿变化、在上升沿采样。本例简化使用clk_12m上升沿,若时序不匹配需改用SCK边沿触发。建议使用PLL生成精确的SCK和clk_12m。
常见坑与排查(阶段3)
- 坑1:WS边沿检测错误——若WS频率为48kHz,而clk_12m为12.288MHz,每个WS周期有256个clk周期,边沿检测应可靠。但若使用异步复位,需确保rst_n释放时WS状态稳定,否则可能误触发。
- 坑2:位序错误——I2S标准是MSB先传,而shift_reg是左移(低位补新数据),最终audio_sample的位序会反转。修正方法:在输出时按位反转,或改用右移寄存器。
阶段4:融合引擎与AXI4-Stream打包
融合模块将视频行数据与音频采样合并为统一数据包。每收到一行视频(640像素),从音频FIFO中读取4个采样,打包输出。
module fusion_engine (
input wire clk_100m,
input wire rst_n,
// 视频输入(来自mipi_rx,已跨时钟域同步)
input wire [7:0] vid_pixel,
input wire vid_valid,
input wire vid_hsync,
input wire vid_vsync,
// 音频输入(来自i2s_rx,已跨时钟域同步)
input wire [15:0] aud_sample,
input wire aud_valid,
// AXI4-Stream 输出
output reg axis_tvalid,
output reg [31:0] axis_tdata,
input wire axis_tready,
output reg axis_tlast
);
// 音频FIFO(存储4个采样)
reg [15:0] audio_fifo [0:3];
reg [1:0] wr_ptr, rd_ptr;
reg [1:0] fifo_cnt;
// 写音频FIFO
always @(posedge clk_100m or negedge rst_n) begin
if (!rst_n) begin
wr_ptr <= 2'd0;
fifo_cnt <= 2'd0;
end else if (aud_valid) begin
audio_fifo[wr_ptr] <= aud_sample;
wr_ptr <= wr_ptr + 1'b1;
fifo_cnt <= fifo_cnt + 1'b1;
end
end
// 状态机:打包一行数据
reg [1:0] state;
localparam IDLE = 2'd0, HEADER = 2'd1, PIXEL = 2'd2, TAIL = 2'd3;
reg [9:0] pixel_cnt; // 0-639
always @(posedge clk_100m or negedge rst_n) begin
if (!rst_n) begin
state <= IDLE;
axis_tvalid <= 1'b0;
axis_tdata <= 32'd0;
axis_tlast <= 1'b0;
rd_ptr <= 2'd0;
pixel_cnt <= 10'd0;
end else begin
case (state)
IDLE: begin
if (vid_hsync) begin // 行同步到来,开始打包
state <= HEADER;
// 从FIFO读取4个音频采样
axis_tdata <= {audio_fifo[0], audio_fifo[1], audio_fifo[2], audio_fifo[3]};
axis_tvalid <= 1'b1;
rd_ptr <= 2'd0;
end
end
HEADER: begin
if (axis_tready) begin
axis_tvalid <= 1'b0;
state <= PIXEL;
pixel_cnt <= 10'd0;
end
end
PIXEL: begin
if (vid_valid) begin
axis_tdata <= {24'd0, vid_pixel};
axis_tvalid <= 1'b1;
if (pixel_cnt == 10'd639) begin
state <= TAIL;
axis_tlast <= 1'b1;
end else begin
pixel_cnt <= pixel_cnt + 1'b1;
end
end else begin
axis_tvalid <= 1'b0;
end
end
TAIL: begin
if (axis_tready) begin
axis_tlast <= 1'b0;
axis_tvalid <= 1'b0;
state <= IDLE;
end
end
default: state <= IDLE;
endcase
end
end
endmodule逐行说明
- 第1-16行:模块端口。视频与音频输入均已通过异步FIFO同步到clk_100m域。AXI4-Stream输出包含tvalid/tdata/tready/tlast标准握手信号。
- 第18-21行:音频FIFO实现。深度4,用于缓存一行时间内的音频采样(每行约37μs,音频采样间隔约20.8μs,可缓存约2个采样,深度4留余量)。
- 第23-31行:写FIFO逻辑。每个aud_valid写入一个采样,wr_ptr递增,fifo_cnt记录已存数量(用于调试)。
- 第33-71行:打包状态机。IDLE等待vid_hsync上升沿;HEADER将4个音频采样打包为32位数据输出;PIXEL逐像素输出(每个像素占32位,高24位补零);TAIL发送tlast信号结束数据包。
- 第47-49行:在HEADER状态,axis_tdata直接拼接4个16位音频采样。注意:这里假设FIFO非空(实际应添加空标志检查,否则可能输出无效数据)。
- 第60-63行:在PIXEL状态,每个vid_valid时钟周期输出一个像素,当pixel_cnt达到639时转入TAIL。
常见坑与排查(阶段4)
- 坑1:音频FIFO空读——若音频采样率低于预期(例如传感器实际为44.1kHz而非48kHz),FIFO可能在一行时间内未填满4个采样,导致读取时输出旧数据。解决方案:在HEADER状态前检查fifo_cnt>=4,否则等待。
- 坑2:AXI4-Stream背压导致丢像素——若下游模块(如DDR写入)未及时处理,axis_tready可能拉低,导致vid_valid期间无法输出像素。需在PIXEL状态添加像素缓存(如简单FIFO)或暂停视频读取。
原理与设计说明
多模态传感器融合在FPGA上实现的核心矛盾是:视频流(高带宽、实时性要求高)与音频流(低带宽、但对连续性敏感)的同步问题。本设计采用“行同步音频缓存”策略:
- 为什么选择行级融合而非帧级?——帧级融合(每帧末尾打包音频)会导致延迟增加一个帧周期(33ms),对于需要低延迟反馈的应用(如声源定位+视频追踪)不可接受。行



