当前位置: 首页 > news >正文

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_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) beginif (!sys_rst_n || !pll_locked) beginreset_counter  <= 16'h0000;system_reset_n <= 1'b0;end else beginif (reset_counter < 16'hFFFF) beginreset_counter  <= reset_counter + 1'b1;system_reset_n <= 1'b0;  // 保持复位状态end else beginsystem_reset_n <= 1'b1;  // 释放复位endendend// ======================// PCIe复位同步 (pcie_user_clk域)// ======================reg [1:0] pcie_rst_sync;always @(posedge pcie_user_clk or negedge sys_rst_n) beginif (!sys_rst_n || !pll_locked) beginpcie_rst_sync  <= 2'b00;pcie_reset_n   <= 1'b0;end else beginpcie_rst_sync  <= {pcie_rst_sync[0], system_reset_n}; // 同步system_reset_npcie_reset_n   <= pcie_rst_sync[1];endend// ======================// DDR3复位同步 (ddr3_user_clk域)// ======================reg [1:0] ddr3_rst_sync;always @(posedge ddr3_user_clk or negedge sys_rst_n) beginif (!sys_rst_n || !pll_locked) beginddr3_rst_sync  <= 2'b00;ddr3_reset_n   <= 1'b0;end else beginddr3_rst_sync  <= {ddr3_rst_sync[0], system_reset_n}; // 同步system_reset_nddr3_reset_n   <= ddr3_rst_sync[1];endend// ======================// 调试LED控制逻辑// ======================always @(*) begindebug_leds = {error_flag,           // 最高位: 错误标志3'b000,              // 中间3位: 保留system_state[3:0]     // 低4位: 系统状态编码};endendmodule
关键问题

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,         // 设备IDinput [15:0]   vendor_id,         // 供应商IDinput [15:0]   subsystem_id,      // 子系统IDinput [15:0]   subsystem_vendor_id, // 子系统供应商ID// PCIe初始化完成标志output reg     pcie_init_done     // PCIe初始化完成 (高有效)
);// 参数定义: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; // 配置空间数据字// 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 IDassign cfg_dw0 = {vendor_id, device_id, 16'h0000};// cfg_dw1: Class Code + Header Type + Revision IDassign cfg_dw1 = {16'h0000, 16'h0000, 8'h06, 8'h04, 8'h00};  // 设备类代码: PCIe桥// cfg_dw2: Subsystem Vendor ID + Subsystem IDassign 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) beginif (!reset_n) begininit_state    <= INIT_RESET;init_counter  <= 16'h0000;pcie_init_done <= 1'b0;init_error    <= 1'b0;end else begincase (init_state)INIT_RESET: begin// 复位状态:等待复位释放if (reset_n) begininit_state <= INIT_WAIT_LT;endendINIT_WAIT_LT: begin// 等待链路训练开始if (link_up) begininit_state <= INIT_WAIT_DLL;end else if (init_counter >= INIT_TIMEOUT) begininit_state <= INIT_RESET;init_error <= 1'b1;end else begininit_counter <= init_counter + 1'b1;endendINIT_WAIT_DLL: begin// 等待数据链路层锁定if (dll_up) begininit_state <= INIT_WAIT_L0;end else if (init_counter >= INIT_TIMEOUT) begininit_state <= INIT_RESET;init_error <= 1'b1;end else begininit_counter <= init_counter + 1'b1;endendINIT_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) begininit_state <= INIT_RESET;init_error <= 1'b1;end else begininit_counter <= init_counter + 1'b1;endendINIT_COMPLETE: begin// 初始化完成,保持状态if (!link_up || !dll_up || ltssm_state != 8'h0F) begin// 如果链路断开,回到等待状态init_state    <= INIT_WAIT_LT;pcie_init_done <= 1'b0;endenddefault: init_state <= INIT_RESET;endcaseendendendmodule
关键问题

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) beginif (!reset_n) beginmmio_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) beginif (!reset_n) beginstart_acq_sync1 <= 0;start_acq_sync2 <= 0;end else beginstart_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) beginif (!reset_n) beginadc_valid_sync1 <= 0;adc_valid_sync2 <= 0;adc_data_ch0_sync <= 0;adc_data_ch1_sync <= 0;end else beginadc_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) beginif (!reset_n) beginstate <= IDLE;buffer_wptr <= 0;buffer_rptr <= 0;data_count <= 0;data_valid <= 0;end else beginstate <= next_state;case (state)IDLE: begin//IDLE 状态:等待 start_acq_sync2 信号,初始化指针和计数器if (start_acq_sync2) beginbuffer_wptr <= 0;buffer_rptr <= 0;data_count <= 0;endendACQUIRING: beginif (adc_valid_sync2 && buffer_wptr < 2046) begin// 存储双通道ADC数据,将两个通道的数据交替存入缓冲区,每次写入后写指针加 2adc_buffer[buffer_wptr]   <= adc_data_ch0_sync;adc_buffer[buffer_wptr+1] <= adc_data_ch1_sync;buffer_wptr <= buffer_wptr + 2;endendPACKING: begin// 打包数据到256位输出 (16个采样点: 8对双通道数据),读指针加 16data_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;endREADY: beginif (data_ready)//等待 data_ready 信号握手data_valid <= 0;endendcaseend
end// 状态机逻辑,定义状态转移条件
always @(*) beginnext_state = state;case (state)IDLE: beginif (start_acq_sync2)next_state = ACQUIRING;endACQUIRING: beginif (buffer_wptr >= 2046)next_state = PACKING;endPACKING: beginnext_state = READY;endREADY: begin//数据被读取后,若缓冲区还有数据则继续打包,若缓冲区已空则标记完成if (data_ready && buffer_rptr >= 2046)next_state = COMPLETE;else if (data_ready)next_state = PACKING;endCOMPLETE: beginif (!start_acq_sync2)next_state = IDLE;endendcase
end// 缓冲区状态
assign buffer_full = buffer_full_int;
assign buffer_empty = buffer_empty_int;always @(posedge user_clk or negedge reset_n) beginif (!reset_n) beginbuffer_full_int <= 0;buffer_empty_int <= 1;end else beginbuffer_full_int <= (buffer_wptr >= 2046);buffer_empty_int <= (buffer_rptr >= buffer_wptr);end
endassign 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 字节突发长度,它表示每次突发传输的数据块数量最大为8input [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) beginif (!reset_n) beginstate <= IDLE;bytes_transferred <= 0;current_addr <= 0;burst_count <= 0;dma_active <= 0;dma_done <= 0;dma_error <= 0;end else beginstate <= next_state;case (state)IDLE: beginif (start_dma) begincurrent_addr <= dest_addr;//将当前传输位置指向目标地址bytes_transferred <= 0;dma_active <= 1;dma_done <= 0;dma_error <= 0;endendCONFIGURE: begin// 配置突发长度ddr3_burst_len <= burst_size;endBURST_START: beginif (ddr3_req_ready) beginddr3_start_addr <= current_addr;  // 记录当前突发的起始地址burst_count <= burst_size;current_addr <= current_addr + (burst_size * 32);  // 256位 = 32字节//bytes_transferred <= bytes_transferred + (burst_size * 32);endendBURST_TRANSFER: begin//处理单个数据块if (ddr3_req_ready && burst_count > 0) beginburst_count <= burst_count - 1;//每完成一个 256 位数据块传输,突发计数器减 1//current_addr <= current_addr + 32;  // 256位 = 32字节bytes_transferred <= bytes_transferred + 32;//在此处累加已传输字节数endendCOMPLETE: begindma_active <= 0;dma_done <= 1;endERROR: begindma_active <= 0;dma_error <= 1;endendcaseend
end// 状态机逻辑
always @(*) beginnext_state = state;case (state)IDLE: beginif (start_dma)next_state = CONFIGURE;endCONFIGURE: beginnext_state = WAIT_DATA;endWAIT_DATA: beginif (data_in_valid)next_state = BURST_START;endBURST_START: beginif (ddr3_req_ready)next_state = BURST_TRANSFER;endBURST_TRANSFER: beginif (ddr3_req_ready && burst_count == 0) beginif (bytes_transferred >= transfer_len)//当实际的传输字节数到达传输长度时next_state = COMPLETE;elsenext_state = WAIT_DATA;endendCOMPLETE: beginif (!start_dma)next_state = IDLE;endERROR: beginif (!start_dma)next_state = IDLE;enddefault: next_state = IDLE;endcase
end// DDR3接口控制
always @(posedge clk or negedge reset_n) beginif (!reset_n) beginddr3_wdata <= 0;ddr3_addr <= 0;ddr3_write_en <= 0;ddr3_req_valid <= 0;end else begincase (state)BURST_START: beginddr3_wdata <= data_in;ddr3_addr <= ddr3_start_addr;  // 使用记录的突发起始地址ddr3_write_en <= 1;ddr3_req_valid <= 1;endBURST_TRANSFER: beginddr3_wdata <= data_in;// 注意:DDR3控制器会自动递增地址,此处无需更新ddr3_addrddr3_write_en <= 1;ddr3_req_valid <= 1;enddefault: beginddr3_wdata <= 0;ddr3_addr <= 0;ddr3_write_en <= 0;ddr3_req_valid <= 0;endendcaseend
end// 数据输入控制
assign data_in_ready = (state == WAIT_DATA || (state == BURST_TRANSFER && burst_count > 0)) && !dma_error;// 错误检测
always @(posedge clk or negedge reset_n) beginif (!reset_n) begindma_error <= 0;end else beginif (bytes_transferred > transfer_len)dma_error <= 1;else if (state == ERROR)dma_error <= 1;end
endendmodule


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) beginif (!system_reset_n) beginsystem_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 begincase (system_state)SYS_IDLE: beginerror_flag <= 0;if (system_reset_n) begin // 系统复位释放system_state <= SYS_INIT;endendSYS_INIT: beginif (pll_locked && ddr3_init_done && pcie_init_done) beginsystem_state <= SYS_WAIT_TRIG;endendSYS_WAIT_TRIG: begin// 触发条件:外部触发或MMIO命令if (mmio_start_acq /*|| external_trig*/) beginsystem_state <= SYS_ACQUIRING;start_acq <= 1; // 启动采集dma_src_addr <= 'h40000000; // 假设采集缓冲区基地址dma_dest_addr <= 'h80000000; // DDR3目标地址dma_len <= 'h10000; // 传输长度(示例:64KB)endendSYS_ACQUIRING: beginstart_acq <= 1; // 保持采集启动if (acq_complete || acq_in_progress) begin // 采集完成或缓冲区满system_state <= SYS_DMA_XFER;start_acq <= 0;start_dma <= 1; // 启动DMAend else if (acq_error) begin // 采集错误system_state <= SYS_ERROR;error_flag <= 1;endendSYS_DMA_XFER: beginstart_dma <= 1; // 保持DMA启动if (dma_done) beginsystem_state <= SYS_WAIT_TRIG;start_dma <= 0;end else if (dma_error) beginsystem_state <= SYS_ERROR;error_flag <= 1;endendSYS_ERROR: beginif (mmio_clear_error || !sys_rst_n) begin // 清除错误或系统复位system_state <= SYS_IDLE;error_flag <= 0;endenddefault: system_state <= SYS_IDLE;endcaseendendendmodule
模块间关键信号连接
  • 时钟与复位

    • 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 作为状态机初始化完成条件。
错误处理
  • 采集错误(acq_error)或 DMA 错误(dma_error)触发 SYS_ERROR,通过 LED 显示错误标志(debug_leds[7])。
  • 主机可通过 MMIO 写入 mmio_clear_error 清除错误状态。
调试与监控
  • debug_leds 低 4 位显示状态机状态(system_state),最高位显示错误标志(error_flag),便于硬件调试。


9. 系统工作流程

  1. 上电与初始化
    • 时钟管理器生成稳定时钟系统控制模块同步复位信号
    • DDR3 和 PCIe 完成初始化后,状态机进入 SYS_WAIT_TRIG(监听主机通过 MMIO 发送的 mmio_start_acq 或外部触发信号。​​​​​​​
    • 此处需注意:

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

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


二、模块间连接关系

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 容量)。


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

相关文章:

PCIe数据采集系统

PCIe数据采集系统 一、模块功能划分与职责 1. 时钟管理模块 (clock_manager) 核心功能&#xff1a; 生成系统所需的多时钟信号&#xff08;100MHz 系统时钟、125MHz PCIe 时钟、200MHz DDR3 时钟&#xff09;。 关键接口&#xff1a; 输入&#xff1a;系统主时钟 sys_clk、P…...

mac docker弹窗提示Docker 启动没有响应

一、原因分析 这台笔记电脑是Mac M3操作系统,安装Docker之后,Docker应用程序一直启动不起来。 二、解决办法 sudo rm /Library/PrivilegedHelperTools/com.docker.vmnetd sudo cp /Applications/Docker.app/Contents/Library/LaunchServices/com.docker.vmnetd /Library/Pri…...

TVS管用万用表测量方法详解(含二极管档使用指南)

点击下面图片带您领略全新的嵌入式学习路线 &#x1f525;爆款热榜 88万阅读 1.6万收藏 TVS管&#xff08;瞬态抑制二极管&#xff09;是一种用于保护电路免受瞬态高电压冲击的半导体器件。其核心功能是通过快速导通将过压钳位在安全范围内。本文将重点介绍如何用万用表测量…...

当下流行的智能体通信协议:MCP、A2A、ANP 分别是什么?

在当前人工智能&#xff08;AI&#xff09;智能体生态系统中&#xff0c;智能体之间的有效沟通至关重要。为了让AI智能体能够高效、安全地协同工作&#xff0c;业界提出了多种通信协议。其中&#xff0c;MCP、A2A 和 ANP 代表了三个关键层级的通信协议&#xff0c;各自应对不同…...

中国近代史2

甲午中日战争&#xff08;1894-1895&#xff09; 1.开始的标志&#xff1a;丰岛海战 2.进程 平壤之战&#xff1a;清军统帅叶志超不战而降&#xff0c;回民将领左宝贵以身殉职 黄海海战&#xff08;大东沟海战&#xff09;&#xff1a;北洋水师黄海遭遇日本舰队&#xff0c;水…...

Ubnutu ADB 无法识别设备的解决方法

1. 正确安装adb 下载地址 2. 检查 Linux 是否识别设备 lsusb通过上述指令&#xff0c;分别查询插入、断开设备的usb设备表&#xff0c;如下所示&#xff1a; # 插入设备 adbc:~$ lsusb Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub Bus 001 Device 011:…...

基于策略的强化学习方法之近端策略优化(PPO)深度解析

PPO&#xff08;Proximal Policy Optimization&#xff09;是一种基于策略梯度的强化学习算法&#xff0c;旨在通过限制策略更新幅度来提升训练稳定性。传统策略梯度方法&#xff08;如REINFORCE&#xff09;直接优化策略参数&#xff0c;但易因更新步长过大导致性能震荡或崩溃…...

前端图形渲染 html+css、canvas、svg和webgl绘制详解,各个应用场景及其区别

在前端开发中&#xff0c;HTMLCSS、Canvas、SVG 和 WebGL 是实现图形渲染的四种常见技术。它们各自具有不同的特点和适用场景。以下是对这四种技术的详细解析&#xff1a; 1. HTML CSS 特点&#xff1a; 主要用于构建网页的结构和样式。通过 CSS 可以实现简单的图形效果&am…...

《Navicat之外的新选择:实测支持国产数据库的SQLynx核心功能解析》

数据库工具生态的新变量 在数据库管理工具领域&#xff0c;Navicat长期占据开发者心智。但随着国产数据库崛起和技术信创需求&#xff0c;开发者对工具的兼容性、轻量化和本土化适配提出了更高要求。近期体验了一款名为SQLynx的国产数据库管理工具&#xff08;麦聪旗下产品&am…...

Elasticsearch 快速入门指南

1. Elasticsearch 简介 Elasticsearch 是一个基于 Lucene 的开源分布式搜索和分析引擎&#xff0c;由 Elastic 公司开发。它具有以下特点&#xff1a; 分布式&#xff1a;可以轻松扩展到数百台服务器&#xff0c;处理 PB 级数据实时性&#xff1a;数据一旦被索引&#xff0c;…...

解决Mawell1.29.2启动SQLException: You have an error in your SQL syntax问题

问题背景 此前在openEuler24.03 LTS环境下的Hive使用了MySQL8.4.2&#xff0c;在此环境下再安装并启动Maxwell1.29.2时出现如下问题 [ERROR] Maxwell: SQLException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version f…...

【Conda】环境应用至JupyterLab

目录 ✅ 步骤一&#xff1a;激活你的 conda 虚拟环境 ✅ 步骤二&#xff1a;安装 ipykernel&#xff08;如果尚未安装&#xff09; ✅ 步骤三&#xff1a;将环境注册为 Jupyter 内核 ✅ 步骤四&#xff1a;启动 JupyterLab 并选择内核 &#x1f9fc; 可选&#xff1a;删除…...

英语六级听力

试卷结构考试内容数量听力理解长对话8*7.1分听力篇章7*7.1分讲话/报道/讲座10*14.2分考点分析 A. 理解明示的信息 理解主旨大意听懂重要信息或特定的细节理解说话人明确表达的观点、态度等B. 理解隐含的信息 推论隐含的意义判断话语的交际功能推断说话人的观点、态度等C. 运用…...

视差计算,求指导

通过SGBM算法算出来的视差图&#xff0c;照片是3072*3072的&#xff0c; numDisparities是112&#xff0c;bloksize是7 不知道怎么调整了&#xff0c;求指导...

各个历史版本mysql/tomcat/Redis/Jdk/Apache/gitlab下载地址

mysql 各版本下载地址&#xff1a; https://downloads.mysql.com/archives/community/ **************************************************************** tomcat 各版本下载地址&#xff1a; https://archive.apache.org/dist/tomcat/ ********************************…...

【Redis】压缩列表

目录 1、背景2、压缩列表【1】底层结构【2】特性【3】优缺点 1、背景 ziplist&#xff08;压缩列表&#xff09;是redis中一种特殊编码的双向链表数据结构&#xff0c;主要用于存储小型列表和哈希表。它通过紧凑的内存布局和特殊的编码方式来节省内存空间。 2、压缩列表 【1…...

计算机网络--第一章(上)

目录 1.计算机网络的概念 2.计算机网络的组成、功能 3.交换 3.1 电路交换 3.2 报文交换 3.3 分组交换 3.4 虚拟电路交换 4.交换的性能分析 4.1 电路交换 4.2 报文交换 4.3 分组交换 4.4 总结 5.计算机网络的分类 5.1 分布范围分类 5.2 传输技术分类 5.3 拓扑结…...

hbit资产收集工具Docker(笔记版)

1. 安装 Docker 在 Kali 系统中&#xff0c;首先更新软件源&#xff0c;并安装 Docker apt-get update && apt-get upgrade && apt-get dist-upgrade apt-get install docker.io docker-compose安装完成后&#xff0c;使用 docker -v 命令验证安装是否成功。…...

套路化编程:C# winform ListView 自定义排序

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 源码指引&#xff1a;github源…...

CSS3 变形

一、CSS3变形&#xff08;Transform&#xff09;是一些效果的集合&#xff0c;有以下几种&#xff1a; ‌平移&#xff08;Translate&#xff09;‌&#xff1a;元素沿水平或垂直方向移动。‌旋转&#xff08;Rotate&#xff09;‌&#xff1a;元素绕某点旋转一定的角度。‌缩放…...

【python基础知识】Day26 函数

一、函数的定义 函数是一段具有特定功能的、可重用的语句组&#xff0c;用函数名来表示。在需要使用函数时&#xff0c;通过函数名进行调用。函数也可以看作一段具有名字的子程序&#xff0c;可以在需要使用它的地方进行调用执行&#xff0c;不需要在每个执行的地方重复编写这些…...

C#中Action的用法

Action 是 C# 中委托的一种&#xff0c;用于封装无返回值的方法。它引用的方法不能有返回值&#xff0c;但可以有零个或多个参数。相比delegate委托&#xff0c;Action 委托的优点是不必显式定义封装无参数过程的委托&#xff0c;使代码更加简洁和易读。 1、delegate-委托 先…...

IOS CSS3 right transformX 动画卡顿 回弹

卡片从右往左滑动&#xff0c;在同时变换 width height right transformX的时候 在某些IPhone机型上 会有卡顿&#xff0c;在Chrome和Android等很多机型都是OK的&#xff0c;包括我的iphone 14 pro max. IOS 18.2 也是好的。但是&#xff0c;新的iPhone16 也会卡&#xff0c;会…...

ruskal 最小生成树算法

https://www.lanqiao.cn/problems/17138/learning/ 并查集ruskal 最小生成树算法 Kruskal 算法是一种用于在加权无向连通图中寻找最小生成树&#xff08;MST&#xff09;的经典算法。其核心思想是基于贪心策略&#xff0c;通过按边权从小到大排序并逐步选择边&#xff0c;确保…...

多系统环境下,如何构建高效的主数据管理体系?

企业信息化建设步伐不断加快&#xff0c;各类业务系统如雨后春笋般涌现&#xff0c;如ERP、CRM、SCM、MES等等。然而&#xff0c;系统繁多也带来了一个棘手的问题&#xff1a;数据孤岛。各系统间数据标准不一、信息不流通、口径不统一&#xff0c;导致企业主数据&#xff08;如…...

Linux515 rsync定时备份

凌晨1时三分进行备份 源码 code: code指定文件夹定时备份rsync到备份机指定文件夹 一.环境配置(code,backup&#xff09; 1.关闭防火墙 设置selinux相关为0 setenforce 0 /etc/selinux/config SELINUXdisable 分别配置 2.设置主机名 3.配置ip地址&#xff08;…...

Claude官方63组提示词模板全解析:从工作到生活的AI应用指南

在当今AI技术飞速发展的时代&#xff0c;大模型如ChatGPT、Claude等已成为我们工作和生活中不可或缺的助手。然而&#xff0c;许多用户发现同样的AI工具在不同人手中能产生截然不同的效果——关键在于提示词(Prompt)的质量。 提示词是与AI沟通的桥梁&#xff0c;好的提示词能…...

C#中的typeof操作符与Type类型:揭秘.NET反射的基础

引言 在C#编程中&#xff0c;反射&#xff08;Reflection&#xff09;是一种强大的机制&#xff0c;它允许我们在运行时检查和操作类型、方法、属性等程序元素。而这种反射能力的核心就是typeof操作符和System.Type类。当我们希望动态加载程序集、创建对象实例、调用方法&…...

鸿蒙OSUniApp 实现的表单验证与提交功能#三方框架 #Uniapp

UniApp 实现的表单验证与提交功能 前言 在移动端应用开发中&#xff0c;表单是用户与应用交互的重要媒介。一个好的表单不仅布局合理、使用方便&#xff0c;还应该具备完善的验证与提交功能&#xff0c;以确保用户输入的数据准确无误。本文将分享如何在 UniApp 中实现表单验证…...

开源的跨语言GUI元素理解8B大模型:AgentCPM-GUI

一、模型概述 AgentCPM-GUI 是由清华大学自然语言处理实验室 (THUNLP) 和 ModelBest 联合开发的开源大模型。该模型基于 MiniCPM-V 架构&#xff0c;拥有 80 亿参数规模&#xff0c;是一个能够直接在终端设备上运行的轻量化智能体。它创新性地将多模态输入与 GUI 操作相结合&a…...

Function Calling

在介绍Function Calling之前我们先了解一个概念,接口。 接口 两种常见接口: 人机交互接口,User Interface,简称 UI应用程序编程接口,Application Programming Interface,简称 API接口能「通」的关键,是两边都要遵守约定。 人要按照 UI 的设计来操作。UI 的设计要符合人…...

星巴克中国要卖在高点

9%能否救70%的急&#xff1f; 作者|古廿 编辑|文昌龙 星巴克中国刚刚回暖&#xff0c;总部出售的计划再次提上日程。 5月15日&#xff0c;外媒又适时放出消息&#xff1a;星巴克将开始出售其在中国的股份。消息人士称&#xff0c;星巴克本周通过一位财务顾问向几位潜在投资…...

Docker实现MySQL数据库主从复制

一、拉取数据库镜像 docker pull mysql:5.7二、创建两个数据库(一主一从模式) mysql01&#xff08;主&#xff09; 1.docker run -d -p 3310:3306 -v /root/mysql/node-1/init:/docker-entrypoinit-initdb.d -v /root/mysql/node-1/config:/etc/mysql/conf.d -v /root/mysq…...

【物联网】基于树莓派的物联网开发【4】——WIFI+SSH远程登录树莓派

使用背景 没有有线网络&#xff0c;无屏幕如何远程登录&#xff1f;程序猫教大家如何通过电脑wifi热点的方式连接树莓派&#xff0c;ssh连接访问树莓派&#xff0c;包括putty开源远程工具进行连接&#xff0c;VNC远程桌面显示。 注&#xff1a;新手建议买一个树莓派配置的显示…...

CentOS7 OpenSSL升级1.1.1w

1.安装依赖 # openssl-3.4.0需要perl-IPC-Cmd perl-Data-Dumper yum -y install gcc* yum -y install perl-IPC-Cmd perl-Data-Dumper 2.备份、卸载旧OpenSSL 查找安装目录并备份 # whereis openssl openssl: /usr/bin/openssl /usr/lib64/openssl /usr/share/man/man1/op…...

高精度降压稳压技术在现代工业自动化中的应用

一、引言 在现代工业自动化的浪潮中&#xff0c;电源管理技术犹如隐藏在精密机械背后的智囊&#xff0c;虽不直接参与生产流程的逻辑决策&#xff0c;却是保障各类自动化设备稳定、高效运行的基石。高精度降压稳压技术&#xff0c;作为电源管理领域的核心分支&#xff0c;聚焦…...

鸿蒙OSUniApp制作动态筛选功能的列表组件(鸿蒙系统适配版)#三方框架 #Uniapp

使用UniApp制作动态筛选功能的列表组件&#xff08;鸿蒙系统适配版&#xff09; 前言 随着移动应用的普及&#xff0c;用户对应用内容检索和筛选的需求也越来越高。在开发跨平台应用时&#xff0c;动态筛选功能已成为提升用户体验的重要组成部分。本文将详细介绍如何使用UniA…...

Qt中控件的Viewport作用

在Qt中&#xff0c;viewport是控件中用于显示内容的一个概念区域&#xff0c;它在可滚动控件中尤为重要。以下是viewport的主要作用和特点&#xff1a; 主要作用 内容显示区域&#xff1a;viewport定义了控件中实际可见的部分&#xff0c;所有内容都在这个区域内显示。 滚动机…...

论文学习_Precise and Accurate Patch Presence Test for Binaries

摘要&#xff1a;打补丁是应对软件漏洞的主要手段&#xff0c;及时将补丁应用到所有受影响的软件上至关重要&#xff0c;然而这一点在实际中常常难以做到&#xff0c;研究背景。因此&#xff0c;准确检测安全补丁是否已被集成进软件发行版本的能力&#xff0c;对于防御者和攻击…...

ubuntu服务器版启动卡在start job is running for wait for...to be Configured

目录 前言 一、原因分析 二、解决方法 总结 前言 当 Ubuntu 服务器启动时&#xff0c;系统会显示类似 “start job is running for wait for Network to be Configured” 或 “start job is running for wait for Plymouth Boot Screen Service” 等提示信息&#xff0c;并且…...

国产数据库工具突围:SQLynx如何解决Navicat的三大痛点?深度体验报告

引言&#xff1a;Navicat的"中国困境" 当开发者面对达梦数据库的存储过程调试&#xff0c;或是在人大金仓中处理复杂查询时&#xff0c;Navicat突然变得力不从心——这不是个例。 真实痛点&#xff1a;某政务系统迁移至OceanBase后&#xff0c;开发团队发现Navicat无…...

牛客网NC21994:分钟计算

牛客网NC21994&#xff1a;分钟计算 &#x1f4dd; 题目描述 输入格式 输入两行&#xff0c;每行包含两个整数&#xff0c;分别表示小时和分钟第一行表示起始时间&#xff0c;第二行表示结束时间 输出格式 输出一个整数&#xff0c;表示两个时间点之间的分钟数 示例 输入…...

全球宠物经济新周期下的亚马逊跨境采购策略革新——宠物用品赛道成本优化三维路径

在全球"孤独经济"与"银发经济"双轮驱动下&#xff0c;宠物用品市场正经历结构性增长。Euromonitor数据显示&#xff0c;2023年全球市场规模突破1520亿美元&#xff0c;其中中国供应链贡献度达38%&#xff0c;跨境电商出口增速连续三年超25%。在亚马逊流量红…...

Tomcat多应用部署与静态资源路径问题全解指南

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家、CSDN平台优质创作者&#xff0c;高级开发工程师&#xff0c;数学专业&#xff0c;10年以上C/C, C#, Java等多种编程语言开发经验&#xff0c;拥有高级工程师证书&#xff1b;擅长C/C、C#等开发语言&#xff0c;熟悉Java常用开…...

128.在 Vue 3 中使用 OpenLayers 实现绘制矩形截图并保存地图区域

&#x1f4cc; 本文将介绍如何在 Vue 3 中使用 OpenLayers 实现&#xff1a; 1&#xff09;用户可在地图上绘制矩形&#xff1b; 2&#xff09;自动截取该区域地图为图片&#xff1b; 3&#xff09;一键保存为本地 PNG 图片。 ✨效果如下图所示 &#x1f9e0;一、前言 在地图类…...

使用 163 邮箱实现 Spring Boot 邮箱验证码登录

使用 163 邮箱实现 Spring Boot 邮箱验证码登录 本文将详细介绍如何使用网易 163 邮箱作为 SMTP 邮件服务器&#xff0c;实现 Spring Boot 项目中的邮件验证码发送功能&#xff0c;并解决常见配置报错问题。 一、为什么需要邮箱授权码&#xff1f; 出于安全考虑&#xff0c;大…...

python处理异常,JSON

异常处理 #异常处理 # 在连接MySQL数据库的过程中&#xff0c;如果不能有效地处理异常&#xff0c;则异常信息过于复杂&#xff0c;对用户不友好&#xff0c;暴露过多的敏感信息 # 所以&#xff0c;在真实的生产环境中&#xff0c; 程序必须有效地处理和控制异常&#xff0c;按…...

原生微信小程序 textarea组件placeholder无法换行的问题解决办法

【问题描述】 微信小程序原生代码&#xff0c;使用文本域&#xff0c;placeholder使用\n 没有效果&#xff0c;网上找了一堆方案说使用 也没有效果 最后在一个前端大佬博客&#xff0c;找到解决办法&#xff0c;CSS设置word-wrap: break-word; white-space: pre-line; 【解决办…...

毕设设计 | 管理系统图例

文章目录 环素1. 登录、注册2. 菜单管理 环素 1. 登录、注册 2. 菜单管理 公告通知 订单管理 会员管理 奖品管理 新增、编辑模块...

激光雷达视觉定位是3D视觉定位吗?

激光雷达视觉定位通常被归类为3D视觉定位&#xff0c;但具体来说&#xff0c;它是融合了激光雷达&#xff08;LiDAR&#xff09;数据和视觉&#xff08;图像&#xff09;数据的多模态3D定位方法。我们可以从几个角度来理解这点&#xff1a; 为什么说它属于3D视觉定位&#xff…...