FPGA数据交互的奇妙之旅:PCle与光纤协同工作探秘

上位机通过PCle把数据发送给FPGA,FPGA打包后通过光纤模块发送出去,同时FPGA保存光纤过来的数据到DDR3中,当DDR3中的数据存够一定数量把DDR3中的数据通过PCle发送给上位机。

在现代高速数据处理系统中,FPGA(现场可编程门阵列)常常扮演着数据桥梁和处理核心的关键角色。今天咱就唠唠上位机、FPGA、DDR3以及光纤模块之间的数据交互那些事儿:上位机通过PCle把数据发送给FPGA,FPGA打包后通过光纤模块发送出去,同时FPGA保存光纤过来的数据到DDR3中,当DDR3中的数据存够一定数量,又把DDR3中的数据通过PCle发送给上位机。这一连串的数据流动,就像一场精心编排的舞蹈,各个部件紧密配合。

上位机通过PCle向FPGA发送数据

PCle(Peripheral Component Interconnect Express)作为一种高速串行计算机扩展总线标准,为上位机与FPGA之间提供了高速可靠的数据传输通道。在软件层面,上位机端一般会使用相应的PCle驱动程序进行数据发送。以Linux系统为例,简单示意代码如下:

c 复制代码
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

#define PCLE_DEVICE "/dev/pcie_device" // 假设的PCle设备文件路径
#define DATA_SIZE 1024 // 发送数据大小

int main() {
    int fd = open(PCLE_DEVICE, O_WRONLY);
    if (fd < 0) {
        perror("Open PCle device failed");
        return -1;
    }

    char data[DATA_SIZE];
    // 填充要发送的数据,这里简单填充为0
    for (int i = 0; i < DATA_SIZE; i++) {
        data[i] = 0;
    }

    ssize_t bytes_written = write(fd, data, DATA_SIZE);
    if (bytes_written!= DATA_SIZE) {
        perror("Write data to PCle failed");
        close(fd);
        return -1;
    }

    close(fd);
    return 0;
}

这段代码中,首先通过open函数打开PCle设备文件,若打开失败则报错并退出。然后定义了要发送的数据数组并填充数据,最后通过write函数将数据写入PCle设备。若写入字节数与期望发送的数据大小不一致,也会报错并退出。

在FPGA端,需要设计相应的PCle接口逻辑来接收数据。这部分逻辑通常使用硬件描述语言(如Verilog或VHDL)实现。以Verilog为例:

verilog 复制代码
module pcie_rx (
    input wire clk,
    input wire rst_n,
    input wire [31:0] pcie_data,
    input wire pcie_valid,
    output reg [31:0] rx_data,
    output reg rx_valid
);

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        rx_data <= 32'b0;
        rx_valid <= 1'b0;
    end else if (pcie_valid) begin
        rx_data <= pcie_data;
        rx_valid <= 1'b1;
    end else begin
        rx_valid <= 1'b0;
    end
end

endmodule

这个模块中,在时钟上升沿或复位信号有效时,若复位信号有效则将接收数据寄存器rxdata**和接收有效信号rx valid清零。当PCle数据有效信号pcievalid**有效时,将PCle传来的数据pcie data存入rxdata**并置rx valid为有效。

FPGA打包数据通过光纤模块发送出去

FPGA接收完上位机数据后,需要对数据进行打包处理,然后通过光纤模块发送出去。光纤模块一般使用高速串行接口进行数据传输,比如常见的SerDes(Serializer/Deserializer)接口。假设我们将接收到的数据按照一定格式封装成帧,Verilog代码示例如下:

verilog 复制代码
module data_pack_and_tx (
    input wire clk,
    input wire rst_n,
    input wire [31:0] rx_data,
    input wire rx_valid,
    output reg [7:0] serdes_data,
    output reg serdes_valid
);

// 假设每4个32位数据组成一帧,这里简单示意
reg [31:0] data_buffer [0:3];
reg [1:0] buffer_index;
reg frame_ready;

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        buffer_index <= 2'b00;
        frame_ready <= 1'b0;
        for (int i = 0; i < 4; i++) begin
            data_buffer[i] <= 32'b0;
        end
    end else if (rx_valid) begin
        data_buffer[buffer_index] <= rx_data;
        buffer_index <= buffer_index + 1;
        if (buffer_index == 2'd3) begin
            frame_ready <= 1'b1;
        end
    end
end

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        serdes_data <= 8'b0;
        serdes_valid <= 1'b0;
    end else if (frame_ready) {
        // 这里简单将帧数据按字节依次输出给SerDes
        serdes_data <= data_buffer[0][7:0];
        serdes_valid <= 1'b1;
        // 后续还需处理将其他字节按序输出等逻辑
    end else {
        serdes_valid <= 1'b0;
    }
end

endmodule

这段代码中,首先定义了一个数据缓冲区databuffer**用于暂存接收到的数据,buffer index记录缓冲区索引,frameready**表示一帧数据是否准备好。在接收数据有效时,将数据存入缓冲区并更新索引,当存够4个数据时置frame ready为有效。然后在frame_ready有效时,将帧数据按字节输出给SerDes接口。

FPGA保存光纤过来的数据到DDR3中

FPGA还需要接收从光纤过来的数据,并保存到DDR3中。DDR3作为一种高速大容量的动态随机存取存储器,需要复杂的控制逻辑。这里我们简单示意数据接收存储逻辑,假设已经有一个DDR3控制器模块ddr3_ctrl,其接口如下:

verilog 复制代码
module ddr3_ctrl (
    input wire clk,
    input wire rst_n,
    input wire [31:0] write_data,
    input wire write_en,
    input wire [29:0] write_addr,
    output reg [31:0] read_data,
    input wire read_en,
    input wire [29:0] read_addr
);

// DDR3控制逻辑实现,这里省略具体内容
// 主要负责与DDR3芯片交互,完成读写操作
endmodule

对于光纤数据接收并写入DDR3的模块,Verilog代码如下:

verilog 复制代码
module fiber_rx_and_write_ddr3 (
    input wire clk,
    input wire rst_n,
    input wire [31:0] fiber_data,
    input wire fiber_valid,
    output reg [31:0] ddr3_write_data,
    output reg ddr3_write_en,
    output reg [29:0] ddr3_write_addr
);

reg [29:0] write_addr_counter;

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) {
        write_addr_counter <= 30'b0;
        ddr3_write_en <= 1'b0;
    } else if (fiber_valid) {
        ddr3_write_data <= fiber_data;
        ddr3_write_en <= 1'b1;
        ddr3_write_addr <= write_addr_counter;
        write_addr_counter <= write_addr_counter + 1;
    } else {
        ddr3_write_en <= 1'b0;
    }
end

endmodule

在这个模块中,当光纤数据有效时,将光纤数据fiberdata**赋值给DDR3写入数据ddr3 writedata*,置写入使能信号ddr3* writeen**为有效,并更新写入地址ddr3 writeaddr*。地址计数器write* addr_counter在每次写入时递增。

DDR3数据存够后通过PCle发送给上位机

当DDR3中的数据存够一定数量,比如存满1024个数据时,就需要将这些数据通过PCle发送给上位机。首先在FPGA端,要设计读取DDR3数据并通过PCle发送的逻辑。

verilog 复制代码
module ddr3_read_and_pcie_tx (
    input wire clk,
    input wire rst_n,
    input wire [31:0] ddr3_read_data,
    input wire ddr3_read_valid,
    output reg [31:0] pcie_tx_data,
    output reg pcie_tx_valid
);

reg [10:0] data_count;
reg start_tx;

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) {
        data_count <= 11'b0;
        start_tx <= 1'b0;
    } else if (ddr3_read_valid) {
        data_count <= data_count + 1;
        if (data_count == 1024) {
            start_tx <= 1'b1;
        }
    }
end

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) {
        pcie_tx_valid <= 1'b0;
    } else if (start_tx) {
        pcie_tx_data <= ddr3_read_data;
        pcie_tx_valid <= 1'b1;
        // 后续还需处理连续发送等逻辑
    } else {
        pcie_tx_valid <= 1'b0;
    }
end

endmodule

这个模块通过datacount**计数DDR3读取的数据数量,当达到1024时置start tx为有效,开始将DDR3读取的数据ddr3readdata通过PCle发送出去,置pcietxvalid为有效表示PCle发送数据有效。

上位机端接收PCle数据的代码与之前发送数据类似,只是操作变为读取:

c 复制代码
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

#define PCLE_DEVICE "/dev/pcie_device" 
#define DATA_SIZE 1024 * 4 // 假设每个数据32位,共1024个数据

int main() {
    int fd = open(PCLE_DEVICE, O_RDONLY);
    if (fd < 0) {
        perror("Open PCle device failed");
        return -1;
    }

    char data[DATA_SIZE];
    ssize_t bytes_read = read(fd, data, DATA_SIZE);
    if (bytes_read!= DATA_SIZE) {
        perror("Read data from PCle failed");
        close(fd);
        return -1;
    }

    close(fd);
    // 对接收到的数据进行处理
    return 0;
}

这段代码打开PCle设备文件用于读取数据,通过read函数读取数据,若读取字节数与期望不一致则报错并退出,最后关闭设备文件。

通过上述一系列代码和逻辑,就实现了上位机、FPGA、DDR3以及光纤模块之间复杂而有序的数据交互,每个环节都紧密相连,共同构建起一个高效的数据处理与传输系统。这也正是FPGA在现代高速数据处理应用中的魅力所在,灵活的逻辑设计能够满足各种不同的数据交互需求。

相关推荐
长安er2 天前
LeetCode 77/216/22组合型回溯法-组合 / 组合总和 III / 括号生成)
数据结构·算法·leetcode·剪枝·回溯
阿正的梦工坊2 天前
PyTorch 权重剪枝中的阈值计算:深入解读 numel() 和 torch.kthvalue()
人工智能·pytorch·剪枝
Thomas_Cai3 天前
YOLOv10剪枝|稀疏训练、基于torch-pruning剪枝以及微调实践
算法·yolo·剪枝·稀疏训练·结构化剪枝
jumu2024 天前
MPC模型预测控制在Matlab Simulink联合仿真中的探索
剪枝
晚风(●•σ )9 天前
【数据结构】——算法设计范式的相关习题
数据结构·算法·贪心算法·深度优先·动态规划·剪枝·广度优先
沧澜sincerely10 天前
蓝桥杯11 路径之谜
c++·蓝桥杯·stl·dfs·剪枝
liu****12 天前
15.自定义类型:联合和枚举
数据结构·c++·剪枝
聊询QQ:6882388614 天前
COMSOL在超声相控阵聚焦仿真中的应用:基于高斯波与正弦波脉冲函数的模型介绍
剪枝
阿_旭14 天前
LAMP剪枝的基本原理与方法简介
算法·剪枝·lamp