PCIe数据采集系统

PCIe数据采集系统

一、模块功能划分与职责

1. 时钟管理模块 (clock_manager)

  • 核心功能
    • 生成系统所需的多时钟信号(100MHz 系统时钟、125MHz PCIe 时钟、200MHz DDR3 时钟)。
  • 关键接口
    • 输入 :系统主时钟 sys_clk、PCIe 差分参考时钟 pcie_refclk_p/n
    • 输出 :各模块所需时钟(pcie_user_clkddr3_user_clk
代码展示
复制代码
module clock_manager (
    // 输入时钟
    input  wire        sys_clk,        // 系统主时钟 (100MHz)
    input  wire        pcie_refclk_p,  // PCIe参考时钟正极
    input  wire        pcie_refclk_n,  // PCIe参考时钟负极
    
    // 输出时钟
    output wire        pcie_user_clk,  // PCIe用户时钟 (125MHz)
    output wire        ddr3_user_clk,  // DDR3用户时钟 (200MHz)
    output wire        pll_locked      // PLL锁定标志
);

    // 内部信号声明
    wire        pcie_refclk;          // 单端PCIe参考时钟
    wire        pll_fb_out;           // PLL反馈信号
    wire        pcie_user_clk_bufg;   // PCIe用户时钟 (BUFGMUX输出)
    wire        ddr3_user_clk_bufg;   // DDR3用户时钟 (BUFGMUX输出)
    
    // =========================
    // 差分时钟缓冲器 (IBUFDS_GTE2)
    // =========================
    IBUFDS_GTE2 #(
        .REFCLK_EN_TX_PATH    ("FALSE"),
        .REFCLK_HROW_CK_SEL   ("REFCLK_HROW_CK0"),
        .REFCLK_ICNTL_RX      ("00")
    ) ibufds_pcie_refclk (
        .O                    (pcie_refclk),
        .ODIV2                (),
        .CEB                  (1'b0),
        .I                    (pcie_refclk_p),
        .IB                   (pcie_refclk_n)
    );
    
    // =========================
    // 时钟生成器 (MMCM/PLL)
    // =========================
    MMCME4_BASE #(
        .BANDWIDTH            ("OPTIMIZED"),
        .CLKOUT0_DIVIDE_F     (8.000),      // 125MHz (1000MHz/8)
        .CLKOUT0_DUTY_CYCLE   (0.500),
        .CLKOUT0_PHASE        (0.000),
        .CLKOUT1_DIVIDE       (5),          // 200MHz (1000MHz/5)
        .CLKOUT1_DUTY_CYCLE   (0.500),
        .CLKOUT1_PHASE        (0.000),
        .CLKIN1_PERIOD        (10.000),     // 100MHz输入周期
        .CLKFBOUT_MULT_F      (10.000),     // VCO = 100MHz * 10 = 1000MHz
        .DIVCLK_DIVIDE        (1),
        .REF_JITTER1          (0.010)
    ) mmcm_inst (
        .CLKOUT0              (pcie_user_clk_bufg),
        .CLKOUT1              (ddr3_user_clk_bufg),
        .CLKFBOUT             (pll_fb_out),
        .LOCKED               (pll_locked),
        .CLKFBIN              (pll_fb_out),
        .CLKIN1               (sys_clk),
        .PWRDWN               (1'b0),
        .RST                  (1'b0)        // 固定为0(复位由system_control处理)
    );
    
    // =========================
    // 全局时钟缓冲器 (BUFGMUX)
    // =========================
    BUFG pcie_user_clk_bufg_inst (
        .I                    (pcie_user_clk_bufg),
        .O                    (pcie_user_clk)
    );
    
    BUFG ddr3_user_clk_bufg_inst (
        .I                    (ddr3_user_clk_bufg),
        .O                    (ddr3_user_clk)
    );

endmodule
关键问题
1.在上述代码中,提到了BUFG 原语。
复制代码
BUFG pcie_user_clk_bufg_inst (.I (pcie_user_clk_bufg), .O (pcie_user_clk));
BUFG ddr3_user_clk_bufg_inst (.I (ddr3_user_clk_bufg), .O (ddr3_user_clk));
  • BUFG (Buffer Global) 是 Xilinx FPGA 中的全局时钟缓冲器原语,用于将时钟信号驱动到 FPGA 的全局时钟网络(Global Clock Network)。
  • 全局时钟网络 的特点:
    • 低延迟:专门设计的低阻抗布线,确保时钟信号同步到达所有触发器。
    • 高驱动能力:能够驱动大量负载(如数千个触发器)而不失真。
    • 平衡延迟:所有时钟路径延迟一致,避免时钟偏移(skew)问题。

为什么需要 BUFG?

  • FPGA 内部有多种时钟网络:
    • 全局时钟网络(由 BUFG 驱动):用于高频、关键路径的时钟(如系统主时钟、PCIe 时钟)。
    • 区域时钟网络:用于局部模块的时钟,驱动能力和延迟次于全局网络。
  • 如果不使用 BUFG,时钟信号可能通过普通布线到达触发器,导致:
    • 时钟延迟增加,影响时序收敛。
    • 不同触发器的时钟到达时间不一致(时钟偏移),引发亚稳态。
  • 本例中,pcie_user_clk_bufgddr3_user_clk_bufg 是 MMCM 生成的时钟信号,但需通过 BUFG 驱动到全局网络,确保:
    • PCIe 模块的所有触发器使用同步的 125MHz 时钟。
    • DDR3 控制器的所有操作在 200MHz 全局时钟下同步执行。

2. 系统控制模块 (system_control)

  • 核心功能
    • 复位同步将 PCIe 物理层复位信号同步到不同时钟域,避免亚稳态。
    • 状态监控接收系统状态和错误标志,通过 LED 实时显示(低 4 位状态,最高位错误)。
  • 关键接口
    • 输入:时钟信号、PCIe 复位信号、DDR3/PCIe 初始化完成标志、系统状态、错误标志。
    • 输出:同步后的复位信号、调试 LED 信号。
代码展示:
复制代码
module system_control (
    // 时钟输入
    input       sys_clk,                // 系统主时钟 (100MHz, sys_clk域)
    input       pcie_user_clk,          // PCIe用户时钟 (125MHz, pcie_user_clk域)
    input       ddr3_user_clk,          // DDR3用户时钟 (200MHz, ddr3_user_clk域)
    
    // 复位输入
    input       pcie_perst_n,           // PCIe物理层复位 (异步, 低有效)
    input       sys_rst_n,              // 系统全局复位 (低有效, 异步)
    input       pll_locked,             // PLL锁定标志 (来自clock_manager)
    
    // 状态监控输入
    input       init_calib_complete,    // DDR3初始化完成标志 (高有效)
    input       pcie_init_done,         // PCIe初始化完成标志 (高有效)
    input [3:0] system_state,           // 系统状态机状态 (来自顶层)
    input       error_flag,             // 系统错误标志 (来自顶层)
    
    // 复位输出
    output reg  pcie_reset_n,           // PCIe域同步复位 (低有效, pcie_user_clk域)
    output reg  system_reset_n,         // 系统级同步复位 (低有效, sys_clk域)
    output reg  ddr3_reset_n,           // DDR3域同步复位 (低有效, ddr3_user_clk域)
    
    // 调试输出
    output reg [7:0] debug_leds         // 8位调试LED (低4位状态, 最高位错误标志)
);

    // 复位计数器 (确保PLL锁定后延时释放复位)
    reg [15:0] reset_counter;
    
    // ======================
    // 系统级复位同步 (sys_clk域)
    // ======================
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if (!sys_rst_n || !pll_locked) begin
            reset_counter  <= 16'h0000;
            system_reset_n <= 1'b0;
        end else begin
            if (reset_counter < 16'hFFFF) begin
                reset_counter  <= reset_counter + 1'b1;
                system_reset_n <= 1'b0;  // 保持复位状态
            end else begin
                system_reset_n <= 1'b1;  // 释放复位
            end
        end
    end
    
    // ======================
    // PCIe复位同步 (pcie_user_clk域)
    // ======================
    reg [1:0] pcie_rst_sync;
    
    always @(posedge pcie_user_clk or negedge sys_rst_n) begin
        if (!sys_rst_n || !pll_locked) begin
            pcie_rst_sync  <= 2'b00;
            pcie_reset_n   <= 1'b0;
        end else begin
            pcie_rst_sync  <= {pcie_rst_sync[0], system_reset_n}; // 同步system_reset_n
            pcie_reset_n   <= pcie_rst_sync[1];
        end
    end
    
    // ======================
    // DDR3复位同步 (ddr3_user_clk域)
    // ======================
    reg [1:0] ddr3_rst_sync;
    
    always @(posedge ddr3_user_clk or negedge sys_rst_n) begin
        if (!sys_rst_n || !pll_locked) begin
            ddr3_rst_sync  <= 2'b00;
            ddr3_reset_n   <= 1'b0;
        end else begin
            ddr3_rst_sync  <= {ddr3_rst_sync[0], system_reset_n}; // 同步system_reset_n
            ddr3_reset_n   <= ddr3_rst_sync[1];
        end
    end
    
    // ======================
    // 调试LED控制逻辑
    // ======================
    always @(*) begin
        debug_leds = {
            error_flag,           // 最高位: 错误标志
            3'b000,              // 中间3位: 保留
            system_state[3:0]     // 低4位: 系统状态编码
        };
    end

endmodule
关键问题

1. 复位优先级管理

  • 最高优先级 :全局复位 sys_rst_n 和 PLL 锁定状态 pll_locked
  • 次优先级 :模块级复位(如 PCIe 物理层复位 pcie_perst_n)。

2. 复位释放顺序

  1. 全局复位 sys_rst_n 释放

  2. PLL锁定 pll_locked 有效

  3. 计数器延时后释放 system_reset_n

  4. 同步释放 pcie_reset_n 和 ddr3_reset_n

3.调试 LED 输出

将系统状态(system_state)和错误标志(error_flag)编码到 LED 输出,便于硬件调试。


3. PCIe 端点模块 (pcie_endpoint)

  • 核心功能
    • 实现 PCIe Gen2 x4 物理层和链路层,处理差分信号收发。
    • 解析 PCIe 事务层包(TLP),支持 MMIO 读写,与主机通信。
    • 完成 PCIe 链路训练,输出初始化完成标志 pcie_init_done
    • 该模块负责处理 PCIe 接口的数据传输和配置,以及与外部设备进行通信。
  • 关键接口
    • 物理层 :差分信号 pcie_rx_p/npcie_tx_p/n
    • 控制层:MMIO 读写信号、地址和数据总线。
代码展示
复制代码
// Artix-7 PCIe Gen2 x4端点模块
module pcie_endpoint_acx750 (
    // 物理层接口
    input       pcie_refclk_p,        // 参考时钟正极性
    input       pcie_refclk_n,        // 参考时钟负极性
    input  [3:0] pcie_rx_p,           // 接收通道正极性 (x4)
    input  [3:0] pcie_rx_n,           // 接收通道负极性 (x4)
    output [3:0] pcie_tx_p,           // 发送通道正极性 (x4)
    output [3:0] pcie_tx_n,           // 发送通道负极性 (x4)
    
    // 时钟与复位
    output      user_clk,             // 用户时钟输出
    input       reset_n,              // 系统复位 (低有效)
    
    // 数据通道接口
    output [127:0] rx_data,           // 接收数据 (128位)
    output [15:0]  rx_be,             // 接收字节使能 (16位,每字节1位)
    output         rx_valid,          // 接收数据有效
    input          rx_ready,          // 接收准备好
    
    input [127:0]  tx_data,           // 发送数据 (128位)
    input [15:0]   tx_be,             // 发送字节使能
    input          tx_valid,          // 发送数据有效
    output         tx_ready,          // 发送准备好
    
    // MMIO接口
    output         mmio_rd,           // MMIO读使能
    output         mmio_wr,           // MMIO写使能
    output [31:0]  mmio_addr,         // MMIO地址
    output [127:0] mmio_wdata,        // MMIO写数据
    input [127:0]  mmio_rdata,        // MMIO读数据
    output         mmio_rvalid,       // MMIO读数据有效
    
    // 配置空间参数
    input [15:0]   device_id,         // 设备ID
    input [15:0]   vendor_id,         // 供应商ID
    input [15:0]   subsystem_id,      // 子系统ID
    input [15:0]   subsystem_vendor_id, // 子系统供应商ID
    
    // PCIe初始化完成标志
    output reg     pcie_init_done     // PCIe初始化完成 (高有效)
);

    // 参数定义: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; // 配置空间数据字
    
    // PCIe状态监控信号
    wire [7:0]  pcie_status;          // PCIe状态寄存器
    wire        link_up;              // 链路建立标志 (高有效)
    wire        dll_up;               // 数据链路层锁定标志 (高有效)
    wire [7:0]  ltssm_state;          // 链路训练状态机状态
    
    // 初始化状态机
    reg [2:0]   init_state;
    localparam  INIT_RESET    = 3'b000,  // 复位状态
                INIT_WAIT_LT  = 3'b001,  // 等待链路训练
                INIT_WAIT_DLL = 3'b010,  // 等待数据链路层
                INIT_WAIT_L0  = 3'b011,  // 等待进入L0状态
                INIT_COMPLETE = 3'b100;  // 初始化完成
    
    // 初始化超时计数器
    reg [15:0]  init_counter;
    parameter   INIT_TIMEOUT  = 16'hFFFF; // 初始化超时值
    
    // 错误标志
    reg         init_error;
    
    // PCIe IP核例化 (Xilinx Artix-7 PCIe Gen2 x4)
    pcie_7x_0 u_pcie_ip (
        // 参考时钟和复位
        .pcie_clk_in_p(pcie_refclk_p),
        .pcie_clk_in_n(pcie_refclk_n),
        .reset(~reset_n),              // 注意:IP核复位为高有效
        
        // 收发器接口
        .rxn(pcie_rx_n),
        .rxp(pcie_rx_p),
        .txn(pcie_tx_n),
        .txp(pcie_tx_p),
        
        // 用户时钟和复位
        .userclk_out(user_clk),
        .userclk2_out(),               // 未使用
        .pclk_out(),                   // 未使用
        
        // 配置空间
        .cfg_dw0(cfg_dw0),
        .cfg_dw1(cfg_dw1),
        .cfg_dw2(cfg_dw2),
        .cfg_dw3(cfg_dw3),
        
        // 数据通道
        .rxdata(rx_data),
        .rxbe(rx_be),
        .rxvalid(rx_valid),
        .rxready(rx_ready),
        .txdata(tx_data),
        .txbe(tx_be),
        .txvalid(tx_valid),
        .txready(tx_ready),
        
        // 配置读写
        .cfg_mrdreq(),                 // 未使用
        .cfg_mwrreq(),                 // 未使用
        .cfg_srdack(),                 // 未使用
        .cfg_swrack(),                 // 未使用
        .cfg_rd(),                      // 未使用
        .cfg_wr(),                      // 未使用
        .cfg_addr(),                    // 未使用
        .cfg_data_in(),                 // 未使用
        .cfg_data_out(),                // 未使用
        
        // 中断
        .intr(),                        // 未使用
        .msi_intr(),                    // 未使用
        .msix_intr(),                   // 未使用
        
        // 连接状态信号
        .pcie_status(pcie_status),
        .link_up(link_up),
        .dll_up(dll_up),
        .ltssm_state(ltssm_state)
    );
    
    // 配置空间初始化
    // cfg_dw0: Vendor ID + Device ID
    assign cfg_dw0 = {vendor_id, device_id, 16'h0000};
    
    // cfg_dw1: Class Code + Header Type + Revision ID
    assign cfg_dw1 = {16'h0000, 16'h0000, 8'h06, 8'h04, 8'h00};  // 设备类代码: PCIe桥
    
    // cfg_dw2: Subsystem Vendor ID + Subsystem ID
    assign cfg_dw2 = {subsystem_vendor_id, subsystem_id, 32'h00000000};
    
    // cfg_dw3: 保留
    assign cfg_dw3 = 64'h0000000000000000;
    
    // MMIO接口控制器例化
    mmio_controller u_mmio_controller (
        .clk(user_clk),
        .reset_n(reset_n),
        .rx_data(rx_data),
        .rx_be(rx_be),
        .rx_valid(rx_valid),
        .rx_ready(rx_ready),
        .tx_data(tx_data),
        .tx_be(tx_be),
        .tx_valid(tx_valid),
        .tx_ready(tx_ready),
        .mmio_rd(mmio_rd),
        .mmio_wr(mmio_wr),
        .mmio_addr(mmio_addr),
        .mmio_wdata(mmio_wdata),
        .mmio_rdata(mmio_rdata),
        .mmio_rvalid(mmio_rvalid)
    );
    
    // PCIe初始化状态机
    always @(posedge user_clk or negedge reset_n) begin
        if (!reset_n) begin
            init_state    <= INIT_RESET;
            init_counter  <= 16'h0000;
            pcie_init_done <= 1'b0;
            init_error    <= 1'b0;
        end else begin
            case (init_state)
                INIT_RESET: begin
                    // 复位状态:等待复位释放
                    if (reset_n) begin
                        init_state <= INIT_WAIT_LT;
                    end
                end
                
                INIT_WAIT_LT: begin
                    // 等待链路训练开始
                    if (link_up) begin
                        init_state <= INIT_WAIT_DLL;
                    end else if (init_counter >= INIT_TIMEOUT) begin
                        init_state <= INIT_RESET;
                        init_error <= 1'b1;
                    end else begin
                        init_counter <= init_counter + 1'b1;
                    end
                end
                
                INIT_WAIT_DLL: begin
                    // 等待数据链路层锁定
                    if (dll_up) begin
                        init_state <= INIT_WAIT_L0;
                    end else if (init_counter >= INIT_TIMEOUT) begin
                        init_state <= INIT_RESET;
                        init_error <= 1'b1;
                    end else begin
                        init_counter <= init_counter + 1'b1;
                    end
                end
                
                INIT_WAIT_L0: begin
                    // 等待进入L0工作状态
                    if (ltssm_state == 8'h0F) begin  // L0状态
                        init_state    <= INIT_COMPLETE;
                        pcie_init_done <= 1'b1;
                    end else if (init_counter >= INIT_TIMEOUT) begin
                        init_state <= INIT_RESET;
                        init_error <= 1'b1;
                    end else begin
                        init_counter <= init_counter + 1'b1;
                    end
                end
                
                INIT_COMPLETE: begin
                    // 初始化完成,保持状态
                    if (!link_up || !dll_up || ltssm_state != 8'h0F) begin
                        // 如果链路断开,回到等待状态
                        init_state    <= INIT_WAIT_LT;
                        pcie_init_done <= 1'b0;
                    end
                end
                
                default: init_state <= INIT_RESET;
            endcase
        end
    end

endmodule
关键问题

1.初始化状态机

复制代码
// 初始化状态机
reg [2:0]   init_state;
localparam  INIT_RESET    = 3'b000,  // 复位状态
            INIT_WAIT_LT  = 3'b001,  // 等待链路训练
            INIT_WAIT_DLL = 3'b010,  // 等待数据链路层
            INIT_WAIT_L0  = 3'b011,  // 等待进入L0状态
            INIT_COMPLETE = 3'b100;  // 初始化完成
  • 功能 :按阶段监控 PCIe 初始化过程,确保各步骤按序完成。

4. MMIO 控制器模块 (mmio_controller)

  • 核心功能
    • 解析主机通过 PCIe 发送的 MMIO命令实现寄存器映射
    • 配置 DMA 参数(源地址、目标地址、传输长度),生成采集启动和错误清除命令。
    • 就是将PCIe的数据直接给外部
  • 关键接口
    • 输入:PCIe MMIO 信号(读写命令、地址、数据)。
    • 输出:DMA 参数、采集启动信号、错误清除信号。

首先需要了解什么是MMIO?

MMIO(Memory-Mapped I/O,内存映射输入输出) 是一种计算机系统中用于外设通信的技术,它将硬件设备的寄存器映射到内存地址空间,使得 CPU 可以像访问内存一样直接读写这些寄存器,从而控制外设或获取外设状态。

核心概念
  1. 地址映射

    • 将外设的控制寄存器、状态寄存器等映射到系统内存地址空间的特定区域。
    • CPU 无需使用特殊的 I/O 指令(如 x86 的 IN/OUT 指令),而是通过普通的内存访问指令(如 LOAD/STORE)与外设交互。
  2. 统一寻址

    • 内存和外设共享同一地址空间,简化了编程模型。
    • 例如,向地址 0x4000_0000 写入数据可能是控制网卡,而读取 0x4000_0004 可能获取其状态。
代码展示
复制代码
// 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,     // 接收准备好
    output [127:0] tx_data,     // 发送数据
    output [15:0]  tx_be,       // 发送字节使能
    output        tx_valid,     // 发送数据有效
    input         tx_ready,     // 发送准备好
    // MMIO接口
    output        mmio_rd,      // MMIO读使能
    output        mmio_wr,      // MMIO写使能
    output [31:0]  mmio_addr,   // MMIO地址
    output [127:0] mmio_wdata,  // MMIO写数据
    input [127:0]  mmio_rdata,  // MMIO读数据
    output        mmio_rvalid   // MMIO读有效
);

// MMIO寄存器映射(示例:假设BAR0基地址0x0000_0000,BAR1基地址0x1000_0000)
//定义设备内部资源的实际起始地址,用于地址解码。
parameter BAR0_BASE = 32'h00000000;
parameter BAR1_BASE = 32'h10000000;

// 内部信号
reg [31:0] mmio_addr_reg;
reg        mmio_rd_reg, mmio_wr_reg;
reg [127:0] mmio_wdata_reg;
reg [127:0] tx_data_reg;
reg [15:0]  tx_be_reg;
reg        tx_valid_reg;

// 地址解码(解析32位MMIO地址)
assign mmio_addr = mmio_addr_reg;
assign mmio_rd = mmio_rd_reg;
assign mmio_wr = mmio_wr_reg;
assign mmio_wdata = mmio_wdata_reg;

// PCIe接收处理(解析TLP包中的MMIO请求)
assign rx_ready = 1'b1;  // 简化设计,假设始终准备好接收

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;
    end else if (rx_valid) begin
        // 解析PCIe接收数据中的MMIO地址和命令(简化示例)
        mmio_addr_reg <= rx_data[31:0];        // 假设前32位为地址
        mmio_wdata_reg <= rx_data[127:32];     // 后96位为写数据(假设32位寄存器访问)
        mmio_rd_reg <= rx_data[128];           // 假设第128位为读命令
        mmio_wr_reg <= rx_data[129];           // 假设第129位为写命令
    end
end

// PCIe发送处理(封装MMIO响应数据)
assign tx_data = {mmio_rdata, mmio_addr_reg};  // 读响应包含数据和地址
assign tx_be = tx_be_reg;
assign tx_valid = tx_valid_reg;

// 读有效信号(假设读操作后一个周期有效)
assign mmio_rvalid = tx_valid;

endmodule

5. 数据采集模块 (data_acquisition)

  • 核心功能
    • 采集双通道 12 位 ADC 数据,打包为 256 位数据块(16 个采样点 / 块)。
    • 管理 2048 深度缓冲区,提供状态标志(缓冲区满 / 空、采集完成、错误)。
  • 关键接口
    • 输入:ADC 数据、有效标志、启动信号、复位信号。
    • 输出:打包后的数据、缓冲区状态、采集完成 / 错误标志。
代码展示
复制代码
//数据采集模块
module data_acquisition (
    input       adc_clk,       // ADC时钟 (40MHz),双通道采集
    input [11:0] adc_data_ch0,  // 通道0数据
    input [11:0] adc_data_ch1,  // 通道1数据
    input       adc_valid,     // ADC数据有效标志
    input       user_clk,      // 用户时钟 (125MHz)
    input       reset_n,
    input       start_acq,     // 开始采集标志位
    output      acq_in_progress,//指示系统目前是否处于采集过程状态
    output      acq_complete,//标志着系统一次完整的数据采集过程已完成
    output reg [255:0] data_out,
    output reg        data_valid,//指示 data_out 中的数据有效,作为数据输出的握手信号
    input         data_ready,//由外部模块发送的就绪信号,表示已准备好接收 data_out,与 data_valid 配合实现握手机制
    output reg [31:0] data_count,//记录已经输出的数据块总量(每成功打包256个数据形成一个数据块并输出就加一),用于之后的数据块索引
    output        buffer_full,//用于指示内部缓冲区即将填满,防止溢出
    output        buffer_empty//指示内部缓冲区已为空,防止无效数据读取
);

// 状态机
localparam IDLE        = 3'd0,//空闲状态,等待开始采集
           ACQUIRING   = 3'd1,//正在采集数据状态
           PACKING     = 3'd2,//打包数据状态
           READY       = 3'd3,//数据打包完成,等待外界形成握手信号用于可以被读取
           COMPLETE    = 3'd4;//表示整个过程完成

reg [2:0]  state, next_state;
reg [15:0] adc_buffer [0:2047];  // 双通道数据缓冲区 (可容纳2048个采样点),用于存储ADC数据
reg [15:0] buffer_wptr;//写指针
reg [15:0] buffer_rptr;//读指针
reg        buffer_full_int;
reg        buffer_empty_int;

// 跨时钟域同步
reg        start_acq_sync1, start_acq_sync2;
reg        adc_valid_sync1, adc_valid_sync2;
reg [11:0] adc_data_ch0_sync;
reg [11:0] adc_data_ch1_sync;

// 同步控制信号到ADC时钟域,使用两级触发器同步 (sync1, sync2) 防止亚稳态
//将 start_acq 信号从 user_clk 同步到 adc_clk 域
always @(posedge adc_clk or negedge reset_n) begin
    if (!reset_n) begin
        start_acq_sync1 <= 0;
        start_acq_sync2 <= 0;
    end else begin
        start_acq_sync1 <= start_acq;
        start_acq_sync2 <= start_acq_sync1;
    end
end

// 同步ADC数据到用户时钟域
//将 ADC 数据和有效信号从 adc_clk 同步到 user_clk 域
always @(posedge user_clk or negedge reset_n) begin
    if (!reset_n) begin
        adc_valid_sync1 <= 0;
        adc_valid_sync2 <= 0;
        adc_data_ch0_sync <= 0;
        adc_data_ch1_sync <= 0;
    end else begin
        adc_valid_sync1 <= adc_valid;
        adc_valid_sync2 <= adc_valid_sync1;
        adc_data_ch0_sync <= adc_data_ch0;
        adc_data_ch1_sync <= adc_data_ch1;
    end
end

// 状态机寄存器
always @(posedge user_clk or negedge reset_n) begin
    if (!reset_n) begin
        state <= IDLE;
        buffer_wptr <= 0;
        buffer_rptr <= 0;
        data_count <= 0;
        data_valid <= 0;
    end else begin
        state <= next_state;
        
        case (state)
            IDLE: begin//IDLE 状态:等待 start_acq_sync2 信号,初始化指针和计数器
                if (start_acq_sync2) begin
                    buffer_wptr <= 0;
                    buffer_rptr <= 0;
                    data_count <= 0;
                end
            end
            
            ACQUIRING: begin
                if (adc_valid_sync2 && buffer_wptr < 2046) begin
                    // 存储双通道ADC数据,将两个通道的数据交替存入缓冲区,每次写入后写指针加 2
                    adc_buffer[buffer_wptr]   <= adc_data_ch0_sync;
                    adc_buffer[buffer_wptr+1] <= adc_data_ch1_sync;
                    buffer_wptr <= buffer_wptr + 2;
                end
            end
            
            PACKING: begin
                // 打包数据到256位输出 (16个采样点: 8对双通道数据),读指针加 16
                data_out <= {adc_buffer[buffer_rptr+15],
                             adc_buffer[buffer_rptr+14],
                             adc_buffer[buffer_rptr+13],
                             adc_buffer[buffer_rptr+12],
                             adc_buffer[buffer_rptr+11],
                             adc_buffer[buffer_rptr+10],
                             adc_buffer[buffer_rptr+9],
                             adc_buffer[buffer_rptr+8],
                             adc_buffer[buffer_rptr+7],
                             adc_buffer[buffer_rptr+6],
                             adc_buffer[buffer_rptr+5],
                             adc_buffer[buffer_rptr+4],
                             adc_buffer[buffer_rptr+3],
                             adc_buffer[buffer_rptr+2],
                             adc_buffer[buffer_rptr+1],
                             adc_buffer[buffer_rptr]};
                buffer_rptr <= buffer_rptr + 16;
                data_valid <= 1;
                data_count <= data_count + 1;
            end
            
            READY: begin
                if (data_ready)//等待 data_ready 信号握手
                    data_valid <= 0;
            end
        endcase
    end
end

// 状态机逻辑,定义状态转移条件
always @(*) begin
    next_state = state;
    
    case (state)
        IDLE: begin
            if (start_acq_sync2)
                next_state = ACQUIRING;
        end
        
        ACQUIRING: begin
            if (buffer_wptr >= 2046)
                next_state = PACKING;
        end
        
        PACKING: begin
            next_state = READY;
        end
        
        READY: begin//数据被读取后,若缓冲区还有数据则继续打包,若缓冲区已空则标记完成
            if (data_ready && buffer_rptr >= 2046)
                next_state = COMPLETE;
            else if (data_ready)
                next_state = PACKING;
        end
        
        COMPLETE: begin
            if (!start_acq_sync2)
                next_state = IDLE;
        end
    endcase
end

// 缓冲区状态
assign buffer_full = buffer_full_int;
assign buffer_empty = buffer_empty_int;

always @(posedge user_clk or negedge reset_n) begin
    if (!reset_n) begin
        buffer_full_int <= 0;
        buffer_empty_int <= 1;
    end else begin
        buffer_full_int <= (buffer_wptr >= 2046);
        buffer_empty_int <= (buffer_rptr >= buffer_wptr);
    end
end

assign acq_in_progress = (state == ACQUIRING);
assign acq_complete = (state == COMPLETE);

endmodule

6. DDR3 控制器模块 (ddr3_controller)

  • 核心功能
    • 封装 Xilinx MIG IP 核,控制 DDR3 存储器的初始化、校准和数据读写。
    • 提供用户接口与 DMA 引擎对接,支持 256 位突发传输。
  • 关键接口
    • 用户接口:数据 / 地址总线、突发长度、读写使能。
    • 物理接口:DDR3 地址 / 控制 / 数据总线(差分时钟、数据选通等)。

这个模块是基于 Xilinx MIG (Memory Interface Generator) IP 核的 DDR3 控制器实现,用于在 FPGA 和 DDR3 存储器之间建立高速数据通道。

MIG (Memory Interface Generator) 是 Xilinx 提供的一个关键 IP 核**,用于为其 FPGA 产品生成针对特定存储器的控制器和物理接口**。它支持多种存储器类型(如 DDR3、DDR4、LPDDR3/4 等),并自动处理复杂的时序和协议,让开发者无需深入了解底层存储器细节即可实现高速数据传输。

主要功能包括:

  • 自动生成符合 JEDEC 标准的存储器控制器
  • 实现高速数据通路(支持高达 3200Mbps 的数据率)
  • 处理初始化、校准、刷新等复杂操作
  • 提供用户友好的抽象接口(如 AXI4、Native 接口)
  • 优化时序以满足特定 FPGA 和存储器组合的要求

在该模块中创建了命令转换逻辑,实现了用户接口和 MIG IP 核之间的信号转换:

复制代码
assign app_clk = ui_clk; // 应用层时钟等于用户接口时钟
assign app_rst_n = !ui_clk_sync_rst && init_calib_complete;

assign app_addr = addr;// 用户地址直接映射到应用层地址
assign app_cmd = write_en ? 3'b000 : 3'b001;  // 000=写, 001=读
assign app_en = req_valid && app_rdy;
assign app_wdf_data = wdata;// 用户写数据映射到写数据FIFO
assign app_wdf_end = app_en && write_en;
assign app_wdf_wren = app_en && write_en;

assign req_ready = app_rdy;// 控制器就绪状态反馈
assign rdata = app_rd_data;// 读取数据输出
assign rdata_valid = app_rd_data_valid;
  1. 时钟和复位处理

    • 使用 MIG 提供的用户接口时钟ui_clk
    • 复位条件结合了同步复位信号和初始化校准完成标志
  2. 读写命令处理

    • 根据write_enread_en生成对应的命令编码
    • 只有当控制器就绪 (app_rdy) 且用户请求有效 (req_valid) 时才发出命令
  3. 写数据处理

    • 将用户提供的 256 位写数据wdata直接传递给 MIG
    • 生成写数据 FIFO 的控制信号
  4. 读数据处理

    • 将 MIG 返回的读数据app_rd_data传递给用户
    • 同步读数据有效标志app_rd_data_valid
代码展示
复制代码
// ACX750 DDR3控制器模块 (基于Xilinx MIG 7 Series)
//这个模块定义了三类接口:
//用户逻辑接口:包括时钟、复位、数据、地址和控制信号
//状态反馈接口:指示数据有效性和控制器状态
//物理 DDR3 接口:连接到实际 DDR3 芯片的电气信号
module ddr3_controller_acx750 (
    input       user_clk,
    input       reset_n,
    input [255:0] wdata,//写入DDR3的数据,256位
    output [255:0] rdata,//从DDR3中读取的数据,256位
    input [31:0]  addr,//读写地址
    input [31:0]  burst_len,//突发传输长度
    input         write_en,
    input         read_en,
    input         req_valid,//请求有效信号
    output        req_ready,//控制器就绪信号
    output        rdata_valid,//读取数据有效信号
    // 物理接口
    output [13:0] ddr3_addr,//DD3地址线
    output [2:0]  ddr3_ba,// DDR3存储体地址
    output        ddr3_ras_n, // 行地址选通信号(低电平有效)
    output        ddr3_cas_n,// 列地址选通信号(低电平有效)
    output        ddr3_we_n,// 写使能信号(低电平有效)
    output [0:0]  ddr3_ck_p,// DDR3时钟(正极性)
    output [0:0]  ddr3_ck_n,// DDR3时钟(负极性)
    output [0:0]  ddr3_cke,// DDR3时钟使能
    output [0:0]  ddr3_odt,// DDR3片上终端匹配
    output [3:0]  ddr3_dm,// DDR3数据掩码
    inout [31:0]  ddr3_dq,// DDR3数据总线
    inout [3:0]   ddr3_dqs_p,// DDR3数据选通(正极性)
    inout [3:0]   ddr3_dqs_n // DDR3数据选通(负极性)
);

// 内部信号,用于连接用户逻辑和 MIG IP 核,包括时钟、复位、数据、地址和控制信号。
wire        app_clk;
wire        app_rst_n;
wire [255:0] app_wdf_data; // 写数据FIFO数据
wire        app_wdf_end; // 写数据FIFO结束指示
wire        app_wdf_wren;
wire [31:0] app_addr;
wire [2:0]  app_cmd;// 应用层命令(000=写,001=读)
wire        app_en;
wire        app_rdy;// 应用层就绪
wire [255:0] app_rd_data;// 读数据
wire        app_rd_data_valid;
wire        app_rd_data_end;
wire        ui_clk;  // 用户接口时钟
wire        ui_clk_sync_rst;// 用户接口同步复位
wire        init_calib_complete;// 初始化校准完成标志

// MIG 7 Series IP核例化
mig_7series_0 u_mig (
    // 时钟和复位
    .sys_clk_i(user_clk),
    .sys_rst_n(reset_n),
    
    // 用户接口
    .app_addr(app_addr),
    .app_cmd(app_cmd),
    .app_en(app_en),
    .app_wdf_data(app_wdf_data),
    .app_wdf_end(app_wdf_end),
    .app_wdf_wren(app_wdf_wren),
    .app_rd_data(app_rd_data),
    .app_rd_data_valid(app_rd_data_valid),
    .app_rd_data_end(app_rd_data_end),
    .app_rdy(app_rdy),
    .app_wdf_rdy(),
    .app_sr_req(),
    .app_ref_req(),
    .app_zq_req(),
    .app_sr_active(),
    .app_ref_ack(),
    .app_zq_ack(),
    .ui_clk(ui_clk),
    .ui_clk_sync_rst(ui_clk_sync_rst),
    .init_calib_complete(init_calib_complete),
    
    // DDR3物理接口
    .ddr3_addr(ddr3_addr),
    .ddr3_ba(ddr3_ba),
    .ddr3_cas_n(ddr3_cas_n),
    .ddr3_ck_n(ddr3_ck_n),
    .ddr3_ck_p(ddr3_ck_p),
    .ddr3_cke(ddr3_cke),
    .ddr3_ras_n(ddr3_ras_n),
    .ddr3_we_n(ddr3_we_n),
    .ddr3_dq(ddr3_dq),
    .ddr3_dqs_n(ddr3_dqs_n),
    .ddr3_dqs_p(ddr3_dqs_p),
    .ddr3_odt(ddr3_odt),
    .ddr3_dm(ddr3_dm),
    
    // 其他信号
    .device_temp(),
    .sys_clk_eb(),
    .user_clk(),
    .user_rst(),
    .calib_done()
);

// 命令转换逻辑
assign app_clk = ui_clk; // 应用层时钟等于用户接口时钟
assign app_rst_n = !ui_clk_sync_rst && init_calib_complete;

assign app_addr = addr;// 用户地址直接映射到应用层地址
assign app_cmd = write_en ? 3'b000 : 3'b001;  // 000=写, 001=读
assign app_en = req_valid && app_rdy;
assign app_wdf_data = wdata;// 用户写数据映射到写数据FIFO
assign app_wdf_end = app_en && write_en;
assign app_wdf_wren = app_en && write_en;

assign req_ready = app_rdy;// 控制器就绪状态反馈
assign rdata = app_rd_data;// 读取数据输出
assign rdata_valid = app_rd_data_valid;

endmodule

这个 DDR3 控制器模块通过 Xilinx MIG IP 核实现了 FPGA 与 DDR3 存储器之间的高速接口。主要特点包括:

  1. 用户友好的抽象接口:提供简单的读写命令、地址和数据接口
  2. 复杂时序自动处理:MIG 负责处理 DDR3 的所有低级时序要求
  3. 突发传输支持 :通过burst_len参数支持高效的批量数据传输
  4. 多时钟域管理:正确处理不同时钟域之间的同步问题
  5. 初始化和校准:自动执行 DDR3 的初始化和校准流程

使用这个模块时,用户只需关注高层的读写操作,而无需关心 DDR3 的底层时序细节,大大简化了高速存储器系统的设计。


7. 高级 DMA 引擎 (dma_engine)

  • 核心功能
    • 实现高速数据传输,将采集模块的缓冲区数据搬移到 DDR3。
    • 支持突发传输模式,优化带宽利用率,提供传输完成和错误标志。
  • 关键接口
    • 输入:启动信号、传输参数(地址、长度、突发大小)。
    • 数据通路:连接采集模块输出和 DDR3 控制器输入。
代码展示
复制代码
//DMA控制模块
module advanced_dma (
    input       clk,
    input       reset_n,
    input       start_dma,//控制信号,启动DMA传输
    input [31:0] src_addr,//源地址
    input [31:0] dest_addr,//目标地址
    input [31:0] transfer_len,//传输长度
    input [7:0]  burst_size,  // 突发配置,2^n 字节突发长度,它表示每次突发传输的数据块数量最大为8
    input [255:0] data_in,//256 位(32 字节),因此每次传输的数据块大小为 32 字节。
    input       data_in_valid,
    output      data_in_ready,
    //下列为DDR3状态接口
    output reg [255:0] ddr3_wdata,
    output reg [31:0]  ddr3_addr,
    output reg [31:0]  ddr3_burst_len,
    output reg         ddr3_write_en,
    output reg         ddr3_req_valid,
    input        ddr3_req_ready,
    output reg         dma_done,
    output reg         dma_error
);

// DMA状态机
localparam IDLE         = 4'd0,//IDLE: 空闲状态,等待启动命令
           CONFIGURE    = 4'd1,//配置DMA参数
           WAIT_DATA    = 4'd2,//等待有效数据输入
           BURST_START  = 4'd3,//开始突发传输
           BURST_TRANSFER = 4'd4,//进行突发传输
           BURST_END    = 4'd5,//结束突发传输
           COMPLETE     = 4'd6,//传输完成状态
           ERROR        = 4'd7;//错误状态

reg [3:0]  state, next_state;
reg [31:0] bytes_transferred;//记录已传输字节数
reg [31:0] current_addr;//指向当前的传输位置
reg [31:0] ddr3_start_addr;  // 记录当前突发的起始地址
reg [7:0]  burst_count;//定义突发剩余数据块计数器
reg        dma_active;//指示DMA是否处于活跃状态

// 状态机寄存器
always @(posedge clk or negedge reset_n) begin
    if (!reset_n) begin
        state <= IDLE;
        bytes_transferred <= 0;
        current_addr <= 0;
        burst_count <= 0;
        dma_active <= 0;
        dma_done <= 0;
        dma_error <= 0;
    end else begin
        state <= next_state;
        
        case (state)
            IDLE: begin
                if (start_dma) begin
                    current_addr <= dest_addr;//将当前传输位置指向目标地址
                    bytes_transferred <= 0;
                    dma_active <= 1;
                    dma_done <= 0;
                    dma_error <= 0;
                end
            end
            
            CONFIGURE: begin
                // 配置突发长度
                ddr3_burst_len <= burst_size;
            end
            
            BURST_START: begin
                if (ddr3_req_ready) begin
                    ddr3_start_addr <= current_addr;  // 记录当前突发的起始地址
                    burst_count <= burst_size;
                    current_addr <= current_addr + (burst_size * 32);  // 256位 = 32字节
                    //bytes_transferred <= bytes_transferred + (burst_size * 32);
                end
            end
            
            BURST_TRANSFER: begin//处理单个数据块
                if (ddr3_req_ready && burst_count > 0) begin
                    burst_count <= burst_count - 1;//每完成一个 256 位数据块传输,突发计数器减 1
                    //current_addr <= current_addr + 32;  // 256位 = 32字节
                    bytes_transferred <= bytes_transferred + 32;//在此处累加已传输字节数
                end
            end
            
            COMPLETE: begin
                dma_active <= 0;
                dma_done <= 1;
            end
            
            ERROR: begin
                dma_active <= 0;
                dma_error <= 1;
            end
        endcase
    end
end

// 状态机逻辑
always @(*) begin
    next_state = state;
    
    case (state)
        IDLE: begin
            if (start_dma)
                next_state = CONFIGURE;
        end
        
        CONFIGURE: begin
            next_state = WAIT_DATA;
        end
        
        WAIT_DATA: begin
            if (data_in_valid)
                next_state = BURST_START;
        end
        
        BURST_START: begin
            if (ddr3_req_ready)
                next_state = BURST_TRANSFER;
        end
        
        BURST_TRANSFER: begin
            if (ddr3_req_ready && burst_count == 0) begin
                if (bytes_transferred >= transfer_len)//当实际的传输字节数到达传输长度时
                    next_state = COMPLETE;
                else
                    next_state = WAIT_DATA;
            end
        end
        
        COMPLETE: begin
            if (!start_dma)
                next_state = IDLE;
        end
        
        ERROR: begin
            if (!start_dma)
                next_state = IDLE;
        end
        
        default: next_state = IDLE;
    endcase
end

// DDR3接口控制
always @(posedge clk or negedge reset_n) begin
    if (!reset_n) begin
        ddr3_wdata <= 0;
        ddr3_addr <= 0;
        ddr3_write_en <= 0;
        ddr3_req_valid <= 0;
    end else begin
        case (state)
             BURST_START: begin
                ddr3_wdata <= data_in;
                ddr3_addr <= ddr3_start_addr;  // 使用记录的突发起始地址
                ddr3_write_en <= 1;
                ddr3_req_valid <= 1;
            end
            
            BURST_TRANSFER: begin
                ddr3_wdata <= data_in;
                // 注意:DDR3控制器会自动递增地址,此处无需更新ddr3_addr
                ddr3_write_en <= 1;
                ddr3_req_valid <= 1;
            end
            
            default: begin
                ddr3_wdata <= 0;
                ddr3_addr <= 0;
                ddr3_write_en <= 0;
                ddr3_req_valid <= 0;
            end
        endcase
    end
end

// 数据输入控制
assign data_in_ready = (state == WAIT_DATA || (state == BURST_TRANSFER && burst_count > 0)) && !dma_error;

// 错误检测
always @(posedge clk or negedge reset_n) begin
    if (!reset_n) begin
        dma_error <= 0;
    end else begin
        if (bytes_transferred > transfer_len)
            dma_error <= 1;
        else if (state == ERROR)
            dma_error <= 1;
    end
end

endmodule

8. 顶层模块(包含系统状态机)

  • 核心功能

    • 协调全系统流程,控制状态转换(初始化→等待触发→采集→DMA→错误处理)。
    • 监控硬件初始化状态,触发采集和 DMA,处理错误恢复。
  • 状态机状态转换

  • SYS_IDLE :等待系统复位释放(system_reset_n 有效)。

  • SYS_INIT :等待 pll_locked(时钟稳定)、ddr3_init_done(DDR3 初始化完成)、pcie_init_done(PCIe 链路完成)。

  • SYS_WAIT_TRIG :监听主机通过 MMIO 发送的 mmio_start_acq 或外部触发信号。

  • SYS_ACQUIRING:启动数据采集模块,填充缓冲区,采集完成后进入 DMA 传输。

  • SYS_DMA_XFER:DMA 引擎将缓冲区数据写入 DDR3,完成后返回等待触发。

  • SYS_ERROR:处理采集或 DMA 错误,通过 MMIO 或复位清除错误。

代码展示
复制代码
module system_top (
    // 系统时钟和复位
    input       sys_clk,        // 系统主时钟 (100MHz)
    input       sys_rst_n,      // 系统复位 (低有效)
    
    // PCIe接口
    input       pcie_refclk_p,  // PCIe参考时钟正极
    input       pcie_refclk_n,  // PCIe参考时钟负极
    input  [3:0] pcie_rx_p,     // PCIe接收通道正极
    input  [3:0] pcie_rx_n,     // PCIe接收通道负极
    output [3:0] pcie_tx_p,     // PCIe发送通道正极
    output [3:0] pcie_tx_n,     // PCIe发送通道负极
    
    // ADC接口
    input [11:0] adc_data_ch0,  // ADC通道0数据
    input [11:0] adc_data_ch1,  // ADC通道1数据
    input       adc_valid,      // ADC数据有效
    output      adc_reset_n,    // ADC复位 (低有效)
    
    // DDR3物理接口
    output [13:0] ddr3_addr,    // DDR3地址总线
    output [2:0]  ddr3_ba,      // DDR3存储体地址
    output        ddr3_ras_n,   // 行地址选通 (低有效)
    output        ddr3_cas_n,   // 列地址选通 (低有效)
    output        ddr3_we_n,    // 写使能 (低有效)
    output [0:0]  ddr3_ck_p,    // DDR3时钟正极
    output [0:0]  ddr3_ck_n,    // DDR3时钟负极
    output [0:0]  ddr3_cke,     // 时钟使能
    output [0:0]  ddr3_odt,     // 片上终端
    output [3:0]  ddr3_dm,      // 数据掩码
    inout [31:0]  ddr3_dq,      // 数据总线
    inout [3:0]   ddr3_dqs_p,   // 数据选通正极
    inout [3:0]   ddr3_dqs_n,   // 数据选通负极
    
    // 调试输出
    output [7:0]  debug_leds     // 调试LED
);

    // ---------------------
    // 状态机定义
    // ---------------------
    localparam SYS_IDLE       = 4'd0,  // 等待复位释放
               SYS_INIT       = 4'd1,  // 等待初始化完成
               SYS_WAIT_TRIG  = 4'd2,  // 等待触发
               SYS_ACQUIRING  = 4'd3,  // 数据采集
               SYS_DMA_XFER   = 4'd4,  // DMA传输
               SYS_ERROR      = 4'd5;  // 错误处理

    reg [3:0] system_state;
    reg error_flag;
    reg start_acq;          // 采集启动信号
    reg start_dma;          // DMA启动信号
    reg [31:0] dma_src_addr; // DMA源地址(采集缓冲区)
    reg [31:0] dma_dest_addr;// DMA目标地址(DDR3)
    reg [31:0] dma_len;      // DMA传输长度
    reg [7:0]  dma_burst;    // DMA突发大小
    
    // ---------------------
    // 模块实例化
    // ---------------------
    // 1. 时钟管理模块
    wire pcie_user_clk;
    wire ddr3_user_clk;
    wire pll_locked;
    clock_manager u_clock_mgr (
        .sys_clk        (sys_clk),
        .pcie_refclk_p  (pcie_refclk_p),
        .pcie_refclk_n  (pcie_refclk_n),
        .pcie_user_clk  (pcie_user_clk),
        .ddr3_user_clk  (ddr3_user_clk),
        .pll_locked     (pll_locked)
    );

    // 2. 系统控制模块
    wire pcie_reset_n;
    wire system_reset_n;
    wire ddr3_reset_n;
    system_control u_sys_ctrl (
        .sys_clk          (sys_clk),
        .pcie_user_clk    (pcie_user_clk),
        .ddr3_user_clk    (ddr3_user_clk),
        .pcie_perst_n     (1'b1),           // 假设无PCIe永久复位,实际需连接硬件
        .sys_rst_n        (sys_rst_n),
        .pll_locked       (pll_locked),
        .init_calib_complete(ddr3_init_done), // DDR3初始化完成
        .pcie_init_done   (pcie_init_done),   // PCIe初始化完成
        .system_state     (system_state),
        .error_flag       (error_flag),
        .pcie_reset_n     (pcie_reset_n),
        .system_reset_n   (system_reset_n),
        .ddr3_reset_n     (ddr3_reset_n),
        .debug_leds       (debug_leds)
    );

    // 3. PCIe端点模块
    wire user_clk;
    wire [127:0] pcie_rx_data;
    wire [15:0]  pcie_rx_be;
    wire        pcie_rx_valid;
    wire        pcie_rx_ready;
    wire [127:0] pcie_tx_data;
    wire [15:0]  pcie_tx_be;
    wire        pcie_tx_valid;
    wire        pcie_tx_ready;
    wire        mmio_rd;
    wire        mmio_wr;
    wire [31:0]  mmio_addr;
    wire [127:0] mmio_wdata;
    wire [127:0] mmio_rdata;
    wire        mmio_rvalid;
    wire        pcie_init_done;
    pcie_endpoint_acx750 u_pcie_ep (
        .pcie_refclk_p    (pcie_refclk_p),
        .pcie_refclk_n    (pcie_refclk_n),
        .pcie_rx_p        (pcie_rx_p),
        .pcie_rx_n        (pcie_rx_n),
        .pcie_tx_p        (pcie_tx_p),
        .pcie_tx_n        (pcie_tx_n),
        .user_clk         (user_clk),
        .reset_n          (system_reset_n), // 系统控制模块的同步复位
        .rx_data          (pcie_rx_data),
        .rx_be            (pcie_rx_be),
        .rx_valid         (pcie_rx_valid),
        .rx_ready         (pcie_rx_ready),
        .tx_data          (pcie_tx_data),
        .tx_be            (pcie_tx_be),
        .tx_valid         (pcie_tx_valid),
        .tx_ready         (pcie_tx_ready),
        .mmio_rd          (mmio_rd),
        .mmio_wr          (mmio_wr),
        .mmio_addr        (mmio_addr),
        .mmio_wdata       (mmio_wdata),
        .mmio_rdata       (mmio_rdata),
        .mmio_rvalid      (mmio_rvalid),
        .device_id        (16'hABCD),       // 示例设备ID
        .vendor_id        (16'h10EE),       // 示例供应商ID
        .subsystem_id     (16'h0001),
        .subsystem_vendor_id(16'h10EE),
        .pcie_init_done   (pcie_init_done)
    );

    // 4. MMIO控制器模块
    wire mmio_start_acq;
    wire mmio_clear_error;
    mmio_controller u_mmio_ctrl (
        .clk            (user_clk),
        .reset_n        (system_reset_n),
        .rx_data        (pcie_rx_data),
        .rx_be          (pcie_rx_be),
        .rx_valid       (pcie_rx_valid),
        .rx_ready       (pcie_rx_ready),
        .tx_data        (pcie_tx_data),
        .tx_be          (pcie_tx_be),
        .tx_valid       (pcie_tx_valid),
        .tx_ready       (pcie_tx_ready),
        .mmio_rd        (mmio_rd),
        .mmio_wr        (mmio_wr),
        .mmio_addr      (mmio_addr),
        .mmio_wdata     (mmio_wdata),
        .mmio_rdata     (mmio_rdata),
        .mmio_rvalid    (mmio_rvalid),
        .mmio_start_acq (mmio_start_acq),   // 主机通过MMIO启动采集
        .mmio_clear_error(mmio_clear_error) // 主机清除错误
    );

    // 5. 数据采集模块
    wire adc_clk = user_clk; // 使用PCIe用户时钟作为ADC时钟(需根据实际ADC调整)
    wire acq_in_progress;
    wire acq_complete;
    wire [255:0] acq_data_out;
    wire data_valid;
    wire data_ready;
    data_acquisition u_data_acq (
        .adc_clk         (adc_clk),
        .adc_data_ch0    (adc_data_ch0),
        .adc_data_ch1    (adc_data_ch1),
        .adc_valid       (adc_valid),
        .user_clk        (user_clk),
        .reset_n         (system_reset_n),
        .start_acq       (start_acq),
        .acq_in_progress (acq_in_progress),
        .acq_complete    (acq_complete),
        .data_out        (acq_data_out),
        .data_valid      (data_valid),
        .data_ready      (data_ready),
        .data_count      (), // 可选连接到MMIO监控
        .buffer_full     (), // 可选错误处理
        .buffer_empty    ()
    );

    // 6. DDR3控制器模块
    wire ddr3_init_done;
    wire [255:0] ddr3_wdata;
    wire [255:0] ddr3_rdata;
    wire [31:0]  ddr3_addr_reg;
    wire [31:0]  ddr3_burst_len;
    wire        ddr3_write_en;
    wire        ddr3_req_valid;
    wire        ddr3_req_ready;
    ddr3_controller_acx750 u_ddr3_ctrl (
        .user_clk       (ddr3_user_clk),
        .reset_n        (ddr3_reset_n),
        .wdata          (ddr3_wdata),
        .rdata          (ddr3_rdata),
        .addr           (ddr3_addr_reg),
        .burst_len      (ddr3_burst_len),
        .write_en       (ddr3_write_en),
        .read_en        (1'b0),          // 本例仅写操作
        .req_valid      (ddr3_req_valid),
        .req_ready      (ddr3_req_ready),
        .rdata_valid    (),
        .ddr3_addr      (ddr3_addr),
        .ddr3_ba        (ddr3_ba),
        .ddr3_ras_n     (ddr3_ras_n),
        .ddr3_cas_n     (ddr3_cas_n),
        .ddr3_we_n      (ddr3_we_n),
        .ddr3_ck_p      (ddr3_ck_p),
        .ddr3_ck_n      (ddr3_ck_n),
        .ddr3_cke       (ddr3_cke),
        .ddr3_odt       (ddr3_odt),
        .ddr3_dm        (ddr3_dm),
        .ddr3_dq        (ddr3_dq),
        .ddr3_dqs_p     (ddr3_dqs_p),
        .ddr3_dqs_n     (ddr3_dqs_n),
        .init_calib_complete(ddr3_init_done)
    );

    // 7. DMA引擎模块
    wire dma_req_ready;
    advanced_dma u_dma_engine (
        .clk             (user_clk),
        .reset_n         (system_reset_n),
        .start_dma       (start_dma),
        .src_addr        (dma_src_addr),
        .dest_addr       (dma_dest_addr),
        .transfer_len    (dma_len),
        .burst_size      (dma_burst),
        .data_in         (acq_data_out),
        .data_in_valid   (data_valid),
        .data_in_ready   (data_ready),
        .ddr3_wdata      (ddr3_wdata),
        .ddr3_addr       (ddr3_addr_reg),
        .ddr3_burst_len  (ddr3_burst_len),
        .ddr3_write_en   (ddr3_write_en),
        .ddr3_req_valid  (ddr3_req_valid),
        .ddr3_req_ready  (ddr3_req_ready),
        .dma_done        (dma_done),
        .dma_error       (dma_error)
    );

    // ---------------------
    // 系统状态机实现
    // ---------------------
    always @(posedge user_clk or negedge system_reset_n) begin
        if (!system_reset_n) begin
            system_state <= SYS_IDLE;
            start_acq <= 0;
            start_dma <= 0;
            error_flag <= 0;
            dma_src_addr <= 0;
            dma_dest_addr <= 0;
            dma_len <= 0;
            dma_burst <= 8; // 默认突发大小8(256字节)
        end else begin
            case (system_state)
                SYS_IDLE: begin
                    error_flag <= 0;
                    if (system_reset_n) begin // 系统复位释放
                        system_state <= SYS_INIT;
                    end
                end

                SYS_INIT: begin
                    if (pll_locked && ddr3_init_done && pcie_init_done) begin
                        system_state <= SYS_WAIT_TRIG;
                    end
                end

                SYS_WAIT_TRIG: begin
                    // 触发条件:外部触发或MMIO命令
                    if (mmio_start_acq /*|| external_trig*/) begin
                        system_state <= SYS_ACQUIRING;
                        start_acq <= 1; // 启动采集
                        dma_src_addr <= 'h40000000; // 假设采集缓冲区基地址
                        dma_dest_addr <= 'h80000000; // DDR3目标地址
                        dma_len <= 'h10000; // 传输长度(示例:64KB)
                    end
                end

                SYS_ACQUIRING: begin
                    start_acq <= 1; // 保持采集启动
                    if (acq_complete || acq_in_progress) begin // 采集完成或缓冲区满
                        system_state <= SYS_DMA_XFER;
                        start_acq <= 0;
                        start_dma <= 1; // 启动DMA
                    end else if (acq_error) begin // 采集错误
                        system_state <= SYS_ERROR;
                        error_flag <= 1;
                    end
                end

                SYS_DMA_XFER: begin
                    start_dma <= 1; // 保持DMA启动
                    if (dma_done) begin
                        system_state <= SYS_WAIT_TRIG;
                        start_dma <= 0;
                    end else if (dma_error) begin
                        system_state <= SYS_ERROR;
                        error_flag <= 1;
                    end
                end

                SYS_ERROR: begin
                    if (mmio_clear_error || !sys_rst_n) begin // 清除错误或系统复位
                        system_state <= SYS_IDLE;
                        error_flag <= 0;
                    end
                end

                default: system_state <= SYS_IDLE;
            endcase
        end
    end

endmodule
模块间关键信号连接
  • 时钟与复位

    • clock_manager 生成 pcie_user_clkddr3_user_clk,同步到 system_control 生成复位信号。
    • system_controlsystem_reset_n 连接到所有模块的复位输入。
  • PCIe 与 MMIO

    • pcie_endpoint_acx750 解析 PCIe 数据,通过 mmio_controller 转换为内部寄存器操作(如 mmio_start_acq)。
  • 数据通路

    • 采集模块 data_acquisitionacq_data_out 连接到 DMA 引擎的 data_in
    • DMA 引擎的 ddr3_wdata 连接到 DDR3 控制器的 wdata,地址和控制信号自动适配。
  • 初始化标志

    • DDR3 控制器的 init_calib_complete 和 PCIe 模块的 pcie_init_done 作为状态机初始化完成条件。
错误处理
  • 采集错误(acq_error)或 DMA 错误(dma_error)触发 SYS_ERROR,通过 LED 显示错误标志(debug_leds[7])。
  • 主机可通过 MMIO 写入 mmio_clear_error 清除错误状态。
调试与监控
  • debug_leds 低 4 位显示状态机状态(system_state),最高位显示错误标志(error_flag),便于硬件调试。

9. 系统工作流程

上电与初始化
  • 时钟管理器生成稳定时钟系统控制模块同步复位信号

  • DDR3 和 PCIe 完成初始化后,状态机进入 SYS_WAIT_TRIG(监听主机通过 MMIO 发送的 mmio_start_acq 或外部触发信号。

  • 此处需注意: SYS_ACQUIRING 的启动逻辑 触发源独立:系统进入采集状态的条件由当前配置的触发源决定。例如:

    • 若配置为软件触发 ,调用mmio_start_acq后立即启动采集,无论是否有数据输入。
    • 若配置为外部触发,收到有效触发信号后启动采集,与数据无关。
    • 若配置为数据触发 ,仅当数据满足条件时启动采集,此时触发条件与数据相关。多触发源共存:部分系统支持多种触发方式组合(如软件触发优先于外部触发),需通过寄存器配置触发优先级。
    • 另外:无输入信号时,采集数据通常不是绝对全 0 ,可能是 接近 0 的噪声值硬件默认状态值 。或者全 0 结果更可能是软件处理的产物(如驱动默认填充),而非物理通道的真实状态。
数据采集触发
  • 主机通过 MMIO 命令或外部硬件触发采集,状态机启动采集模块,填充缓冲区(在数据采集模块)。
DMA 传输
  • 采集完成后,DMA 引擎将数据以突发模式写入 DDR3利用 DDR3 控制器的高速接口提升带宽
错误恢复
  • 任何阶段出错时进入错误状态,通过复位或主机命令恢复,确保系统可靠运行。

二、模块间连接关系

1. 时钟与复位信号流

复制代码
时钟管理模块 → 系统控制模块 → 所有其他模块  
   (生成125MHz/200MHz时钟和同步复位)  
  • 时钟

    • pcie_user_clk → PCIe 端点、MMIO 控制器、数据采集、DMA 引擎
    • ddr3_user_clk → DDR3 控制器
    • sys_clk → 系统控制模块(复位同步)
  • 复位

    • pcie_reset_n → PCIe 端点、MMIO 控制器、数据采集、DMA 引擎
    • system_reset_n → DDR3 控制器
    • ddr3_reset_n → DDR3 控制器(内部使用)

2. 数据通路连接

复制代码
ADC → 数据采集模块(缓冲区) → DMA引擎 → DDR3控制器 → DDR3存储器  
       (256位数据块)          (突发传输)         (物理层信号)  
  • 采集模块输出acq_data_out(256 位)→ DMA 引擎 data_in
  • DMA 输出ddr3_wdata(256 位)→ DDR3 控制器 app_wdf_data
  • DDR3 物理接口:地址 / 控制信号由 DDR3 控制器生成,连接到 DDR3 芯片

3. 控制信号流

复制代码
主机(PCIe) → MMIO控制器 → 系统状态机 → 数据采集/DMA引擎  
   (MMIO命令)   (解析为启动/配置信号)  (触发采集/传输)  
  • MMIO 命令:主机通过 PCIe 发送读写命令,MMIO 控制器解析后生成:

    • mmio_start_acq:启动数据采集
    • mmio_clear_error:清除错误标志
    • DMA 参数(src_addrdest_addrtransfer_len
  • 状态机输出

    • start_dma:启动 DMA 传输
    • 状态标志(system_stateerror_flag)→ 系统控制模块(LED 显示)

4. 初始化与状态监控

复制代码
DDR3控制器 → 系统控制模块(`init_calib_complete`)  
PCIe端点 → 系统控制模块(`pcie_init_done`)  
  • 系统控制模块接收这两个标志,通知状态机进入SYS_WAIT_TRIG状态。

三、系统总体目标与技术亮点

1. 总体目标

实现一个 高速数据采集与存储系统,具备以下能力:

  • 数据采集:双通道 12 位 ADC,40MSPS 采样率,实时打包为 256 位数据块。
  • 高速存储:通过 DMA 将数据以突发模式写入 DDR3(512MB,1333MHz),带宽≥1GB/s。
  • 主机通信:通过 PCIe Gen2 x4 接口,支持主机配置参数、启动采集、读取状态。
  • 可靠性:多时钟域复位同步、缓冲区背压控制、错误检测与恢复。

2. 技术亮点

  • 多时钟域设计:通过 PLL 生成专用时钟,满足 PCIe 和 DDR3 的严格时序要求。
  • 突发传输优化:DMA 与 DDR3 控制器配合,减少传输开销,提升带宽利用率。
  • 模块化架构:各功能模块独立,便于调试和替换(如更换 ADC 型号或升级 DDR3 容量)。

**具体详细讲解可参考我的后续文章!**​​​​​​​

相关推荐
WKJay_10 分钟前
深入理解 Cortex-M3 特殊寄存器
stm32·单片机·嵌入式硬件
z人间防沉迷k1 小时前
堆(Heap)
开发语言·数据结构·笔记·python·算法
hy.z_7771 小时前
【数据结构】链表 LinkedList
java·数据结构·链表
ROCKY_8171 小时前
数据结构——例题3
数据结构
ROCKY_8172 小时前
数据结构——例题2
数据结构
Akiiiira2 小时前
【数据结构】队列
java·开发语言·数据结构
Linux嵌入式木子2 小时前
# 2-STM32F103-复位和时钟控制RCC
stm32·单片机·嵌入式硬件
zly88653723 小时前
MMIO机制详解
fpga开发
小智学长 | 嵌入式4 小时前
单片机-STM32部分:13-1、编码器
单片机·嵌入式硬件
XiaoyaoCarter4 小时前
每日一道leetcode(新学数据结构版)
数据结构·c++·算法·leetcode·职场和发展·哈希算法·前缀树