PCIe数据采集系统详解
PCIe数据采集系统详解
在上篇文章中,废了老大劲儿我们写出了PCIe数据采集系统;其中各个模块各司其职,相互配合。完成了从数据采集到高速存储到DDR3的全过程。今天我们呢就来详细讲解他们之间的关系?以及各个模块的关键点?他们到底是怎么协同工作的?
总体模块图
各模块关键点
1. 在第一个时钟管理模块
有用到:差分时钟缓冲器,MMCM,全局时钟缓冲器
差分时钟缓冲器将PCIe的差分信号转换为单端信号展示,输出但不单列;
MMCM产生 pcie_user_clk_bufg 和 ddr3_user_clk_bufg ,同时产生 pll_locked锁存;
全局时钟缓冲器将pcie_user_clk_bufg 和 ddr3_user_clk_bufg原语应用于全局网络并输出相应信号;
2.在第二个系统控制模块
其主要目的是实现将异步复位信号同步,输出三个复位信号(系统,PCIe,DDR3;均使用了两个同步寄存器打拍同时配合PLL锁存信号)优势有以下:
- 消除亚稳态风险,提高系统稳定性。
- 简化时序分析,降低设计实现难度。
- 精确控制复位释放时序,确保多时钟域系统协调工作。
- 增强抗干扰能力,避免复位毛刺引发的问题。
在该模块体现为:确保 PCIe 模块在其专用时钟(pcie_user_clk
)稳定后同步释放复位,避免 PCIe 链路初始化错误;控制 DDR3 控制器的复位时序,配合 DDR3 初始化校准流程(init_calib_complete信号),确保内存控制器正确启动。
并且该模块输入一些系统标志如:pcie_perst_n(PCIe物理层复位);init_calib_complete(DDR3初始化完成标志)等,以供相应模块使用,在本系统控制模块未处理,但是经由属于系统的控制部分所以列出。
接收系统状态和错误标志,通过 LED 实时显示
3.第三个PCIe模块
出现了user_clk(125M)user_clk(125M)user_clk(125M)use从r_clk(125M
必须与第一个时钟管理模块进行区分的是:pcie_user_clk(125M);
pcie_user_clk
是系统为 PCIe 模块提供的时钟源。user_clk
是 PCIe IP 核向用户逻辑输出的同步时钟,确保数据在用户逻辑和 PCIe 之间正确传输。-
user_clk
的特殊作用:PCIe IP 核内部会根据链路状态(如 Gen1/Gen2/Gen3)动态调整时钟相位和频率。user_clk
是 IP 核内部同步后的时钟,确保与 PCIe 事务时序(非事务层)严格对齐。
PCIe 端点模块的内部结构: PCIe 端点 IP 核通常包含:
- 物理层(PHY):处理电气信号,使用参考时钟(如 100MHz 差分时钟)。
- 链路层 / 事务层:处理 TLP(事务层包),使用系统提供的
pcie_user_clk
。 - 用户接口:提供与用户逻辑通信的时钟(即
user_clk
)。
在该模块中,输入的是差分信号与复位信号;主要输出用户时钟,MMIO接口与链路完成标志。
数据通道采用 128 位宽接口(PCIe Gen2 标准),带字节使能控制;
// 参数定义:PCIe基地址寄存器(BAR)大小parameter BAR0_SIZE = 16'h1000; // 4KBparameter BAR1_SIZE = 16'h4000; // 16KB// 内部信号wire pcie_clk; // PCIe时钟wire pcie_rst_n; // PCIe复位 (低有效)wire [63:0] cfg_dw0, cfg_dw1, cfg_dw2, cfg_dw3; // 配置空间数据字
- BAR 寄存器:定义 PCIe 配置空间中内存映射区域的大小
- 配置空间:PCIe 设备通过 64 字节配置空间向主机暴露参数
PCIe 设备通过一个256 字节的配置空间向主机暴露自身参数,类似一个 "设备档案"。
主机通过读取这个空间了解设备信息(如厂商、功能、支持的 BAR 数量等),并配置设备行为。
-
前 64 字节(标准头):所有 PCIe 设备必须实现,包含通用信息
- 设备标识:Vendor ID、Device ID、Class Code 等
- 基地址寄存器 (BAR):定义设备可访问的内存 / IO 空间,存储主机分配给设备的基地址
- 中断配置:IRQ 线、MSI/MSI-X 支持
-
后 192 字节:可选扩展区域,用于高级功能(如 PCIe 能力结构)
为什么需要 BAR?
- 地址解耦:设备无需预先知道主机分配的地址,通过 BAR 动态映射
- 资源管理:主机可根据系统情况灵活分配地址空间
- 多设备共存:多个 PCIe 设备可共享同一地址空间,通过 BAR 区分
- 安全隔离:主机可限制设备访问的地址范围,提高系统安全性
同时在MMIO控制器例化部分,实现了将 PCIe 事务转换为 FPGA 内部存储器读写操作(存储器映射)
PCIe 初始化流程详解
PCIe 设备初始化需要完成复杂的状态转换,代码中的状态机对应以下关键阶段:
-
复位阶段 (INIT_RESET)
- 等待系统复位释放
- 初始化内部计数器和标志位
-
链路训练阶段 (INIT_WAIT_LT)
- 监测
link_up
信号,确认物理层连接建立 - 超时处理防止链路训练失败导致的死锁
- 监测
-
数据链路层初始化 (INIT_WAIT_DLL)
- 等待数据链路层锁定(
dll_up
信号) - 建立可靠的数据传输通道
- 等待数据链路层锁定(
-
进入 L0 工作状态 (INIT_WAIT_L0)
- 确认 LTSSM 状态机进入 L0 状态(正常工作状态)
- 完成 PCIe 初始化的最后阶段
-
初始化完成 (INIT_COMPLETE)
- 设置
pcie_init_done
标志,通知系统 PCIe 链路就绪 - 持续监测链路状态,确保连接稳定
- 设置
4.第四个MMIO控制器模块
输入了MMIO数据/地址,实现了 PCIe 总线与 FPGA 内部逻辑之间的 MMIO (存储器映射 I/O) 控制器,负责解析 PCIe 事务层包 (TLP) 并转换为内部寄存器访问。
在该模块编写中,就是使用了正常的always逻辑进行数据的解析打包与传输,没有任何技巧(简化了设计),同时定义了两个内存映射区域的基地址;提供了基础的 PCIe 到内部寄存器的映射功能,但缺少:
完整的TLP解析逻辑,BAR地址范围审查,背压机制(发送通道就绪时才能接收新数据),同时MMIO的错误清楚机制并没有撰写;后续可根据具体的项目要求改进。
示例代码如下:
module mmio_controller (input clk, // 用户时钟 (125MHz)input reset_n, // 复位信号// PCIe数据通道input [127:0] rx_data, // 接收数据input [15:0] rx_be, // 接收字节使能input rx_valid, // 接收数据有效output rx_ready, // 接收准备好// 省略其他接口...// 新增错误处理接口input [7:0] error_flags, // 外部错误标志输入output mmio_clear_error, // 清除错误命令output [7:0] active_errors // 当前活跃错误
);// 寄存器地址定义localparam ERROR_STATUS_REG = 32'h0000_0004; // 错误状态寄存器localparam ERROR_CLEAR_REG = 32'h0000_0008; // 错误清除寄存器// 内部信号reg [31:0] mmio_addr_reg;reg mmio_rd_reg, mmio_wr_reg;reg [127:0] mmio_wdata_reg;reg [7:0] error_status; // 错误状态寄存器reg clear_error_cmd; // 清除错误命令暂存// 错误状态更新always @(posedge clk or negedge reset_n) beginif (!reset_n) beginerror_status <= 8'h00;end else begin// 错误标志可以被设置,但只能通过写清除寄存器清除error_status <= error_status | error_flags;// 处理错误清除命令if (mmio_wr_reg && (mmio_addr_reg == ERROR_CLEAR_REG)) beginerror_status <= error_status & (~mmio_wdata_reg[7:0]);endendend// 地址解码增强always @(posedge clk or negedge reset_n) beginif (!reset_n) beginmmio_addr_reg <= 0;mmio_rd_reg <= 0;mmio_wr_reg <= 0;mmio_wdata_reg <= 0;clear_error_cmd <= 0;end else if (rx_valid) begin// 解析PCIe接收数据中的MMIO地址和命令mmio_addr_reg <= rx_data[31:0];mmio_wdata_reg <= rx_data[127:32];mmio_rd_reg <= rx_data[128];mmio_wr_reg <= rx_data[129];// 检测错误清除命令clear_error_cmd <= mmio_wr_reg && (mmio_addr_reg == ERROR_CLEAR_REG);endend// 输出错误清除信号(脉冲有效)assign mmio_clear_error = clear_error_cmd;// 输出当前活跃错误assign active_errors = error_status;// 其他代码保持不变...
endmodule
5.第五个数据采集模块
输入了开始采集命令start_acq(ctrl);输出标志是采集完成信号;
其功能是双通道采集数据,然后打包数据块进行输出,以适应于高速传输模式;
前面单独出过一篇文章来讲解。
6.第六个DMA引擎控制模块
输入的是用户时钟(125M),数据输入,DMA启动信号;主要输出DDR3的写数据;其核心功能是实现高速数据传输,将采集模块的缓冲区数据搬移到 DDR3。
本模块的关键点主要是突发模式的配置与高速传输状态接收
前面单独出过一篇文章来讲解。
7. 第七个DDR3控制器模块
输入的是时钟ddr3_user_clk(200M),输出init_calib_complete信号,代表DDR3初始化校准完成就绪;
其功能是控制 DDR3 存储器的初始化、校准和数据读写。
实际上在代码编写时,主要是将Xilinx MIG IP 核在程序中例化,进而将DDR3物理层接口出现;
实现了用户接口和 MIG IP 核之间的信号转换
在第八个顶层模块中,实现了对前面模块的例化,同时创建了系统状态机用于协调管理各个模块,见上篇文章!!!