PCIe数据采集系统
一、模块功能划分与职责
1. 时钟管理模块 (clock_manager
)
- 核心功能 :
- 生成系统所需的多时钟信号(100MHz 系统时钟、125MHz PCIe 时钟、200MHz DDR3 时钟)。
- 关键接口 :
- 输入 :系统主时钟
sys_clk
、PCIe 差分参考时钟pcie_refclk_p/n
、 - 输出 :各模块所需时钟(
pcie_user_clk
、ddr3_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_bufg
和ddr3_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. 复位释放顺序
-
全局复位 sys_rst_n 释放
-
PLL锁定 pll_locked 有效
-
计数器延时后释放 system_reset_n
-
同步释放 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/n
、pcie_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 可以像访问内存一样直接读写这些寄存器,从而控制外设或获取外设状态。
核心概念
-
地址映射
- 将外设的控制寄存器、状态寄存器等映射到系统内存地址空间的特定区域。
- CPU 无需使用特殊的 I/O 指令(如 x86 的
IN/OUT
指令),而是通过普通的内存访问指令(如LOAD/STORE
)与外设交互。
-
统一寻址
- 内存和外设共享同一地址空间,简化了编程模型。
- 例如,向地址
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;
-
时钟和复位处理:
- 使用 MIG 提供的用户接口时钟
ui_clk
- 复位条件结合了同步复位信号和初始化校准完成标志
- 使用 MIG 提供的用户接口时钟
-
读写命令处理:
- 根据
write_en
和read_en
生成对应的命令编码 - 只有当控制器就绪 (
app_rdy
) 且用户请求有效 (req_valid
) 时才发出命令
- 根据
-
写数据处理:
- 将用户提供的 256 位写数据
wdata
直接传递给 MIG - 生成写数据 FIFO 的控制信号
- 将用户提供的 256 位写数据
-
读数据处理:
- 将 MIG 返回的读数据
app_rd_data
传递给用户 - 同步读数据有效标志
app_rd_data_valid
- 将 MIG 返回的读数据
代码展示
// 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 存储器之间的高速接口。主要特点包括:
- 用户友好的抽象接口:提供简单的读写命令、地址和数据接口
- 复杂时序自动处理:MIG 负责处理 DDR3 的所有低级时序要求
- 突发传输支持 :通过
burst_len
参数支持高效的批量数据传输 - 多时钟域管理:正确处理不同时钟域之间的同步问题
- 初始化和校准:自动执行 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_clk
和ddr3_user_clk
,同步到system_control
生成复位信号。system_control
的system_reset_n
连接到所有模块的复位输入。
-
PCIe 与 MMIO:
pcie_endpoint_acx750
解析 PCIe 数据,通过mmio_controller
转换为内部寄存器操作(如mmio_start_acq
)。
-
数据通路:
- 采集模块
data_acquisition
的acq_data_out
连接到 DMA 引擎的data_in
。 - DMA 引擎的
ddr3_wdata
连接到 DDR3 控制器的wdata
,地址和控制信号自动适配。
- 采集模块
-
初始化标志:
- DDR3 控制器的
init_calib_complete
和 PCIe 模块的pcie_init_done
作为状态机初始化完成条件。
- DDR3 控制器的
错误处理
- 采集错误(
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_addr
、dest_addr
、transfer_len
)
-
状态机输出:
start_dma
:启动 DMA 传输- 状态标志(
system_state
、error_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 容量)。
**具体详细讲解可参考我的后续文章!**
