PCIe数据采集系统详解

发布于:2025-05-17 ⋅ 阅读:(21) ⋅ 点赞:(0)

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锁存信号)优势有以下:

  1. 消除亚稳态风险,提高系统稳定性。
  2. 简化时序分析,降低设计实现难度。
  3. 精确控制复位释放时序,确保多时钟域系统协调工作。
  4. 增强抗干扰能力,避免复位毛刺引发的问题。

在该模块体现为:确保 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;   // 4KB
    parameter 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; // 配置空间数据字
  1. BAR 寄存器:定义 PCIe 配置空间中内存映射区域的大小
  2. 配置空间:PCIe 设备通过 64 字节配置空间向主机暴露参数

PCIe 设备通过一个256 字节的配置空间向主机暴露自身参数,类似一个 "设备档案"。
主机通过读取这个空间了解设备信息(如厂商、功能、支持的 BAR 数量等),并配置设备行为。

  1. 前 64 字节(标准头):所有 PCIe 设备必须实现,包含通用信息

    • 设备标识:Vendor ID、Device ID、Class Code 等
    • 基地址寄存器 (BAR):定义设备可访问的内存 / IO 空间,存储主机分配给设备的基地址
    • 中断配置:IRQ 线、MSI/MSI-X 支持
  2. 后 192 字节:可选扩展区域,用于高级功能(如 PCIe 能力结构)

为什么需要 BAR?

  1. 地址解耦:设备无需预先知道主机分配的地址,通过 BAR 动态映射
  2. 资源管理:主机可根据系统情况灵活分配地址空间
  3. 多设备共存:多个 PCIe 设备可共享同一地址空间,通过 BAR 区分
  4. 安全隔离:主机可限制设备访问的地址范围,提高系统安全性

同时在MMIO控制器例化部分,实现了将 PCIe 事务转换为 FPGA 内部存储器读写操作(存储器映射)

PCIe 初始化流程详解

PCIe 设备初始化需要完成复杂的状态转换,代码中的状态机对应以下关键阶段:

  1. 复位阶段 (INIT_RESET)

    • 等待系统复位释放
    • 初始化内部计数器和标志位
  2. 链路训练阶段 (INIT_WAIT_LT)

    • 监测link_up信号,确认物理层连接建立
    • 超时处理防止链路训练失败导致的死锁
  3. 数据链路层初始化 (INIT_WAIT_DLL)

    • 等待数据链路层锁定(dll_up信号)
    • 建立可靠的数据传输通道
  4. 进入 L0 工作状态 (INIT_WAIT_L0)

    • 确认 LTSSM 状态机进入 L0 状态(正常工作状态)
    • 完成 PCIe 初始化的最后阶段
  5. 初始化完成 (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) begin
        if (!reset_n) begin
            error_status <= 8'h00;
        end else begin
            // 错误标志可以被设置,但只能通过写清除寄存器清除
            error_status <= error_status | error_flags;
            
            // 处理错误清除命令
            if (mmio_wr_reg && (mmio_addr_reg == ERROR_CLEAR_REG)) begin
                error_status <= error_status & (~mmio_wdata_reg[7:0]);
            end
        end
    end
    
    // 地址解码增强
    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            mmio_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);
        end
    end
    
    // 输出错误清除信号(脉冲有效)
    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 核之间的信号转换


在第八个顶层模块中,实现了对前面模块的例化,同时创建了系统状态机用于协调管理各个模块,见上篇文章!!!


网站公告

今日签到

点亮在社区的每一天
去签到