news 2026/2/1 2:01:18

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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系统为例,简单示意代码如下:

#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为例:

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和接收有效信号rxvalid清零。当PCle数据有效信号pcievalid有效时,将PCle传来的数据pciedata存入rxdata并置rxvalid为有效。

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

FPGA接收完上位机数据后,需要对数据进行打包处理,然后通过光纤模块发送出去。光纤模块一般使用高速串行接口进行数据传输,比如常见的SerDes(Serializer/Deserializer)接口。假设我们将接收到的数据按照一定格式封装成帧,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用于暂存接收到的数据,bufferindex记录缓冲区索引,frameready表示一帧数据是否准备好。在接收数据有效时,将数据存入缓冲区并更新索引,当存够4个数据时置frameready为有效。然后在frame_ready有效时,将帧数据按字节输出给SerDes接口。

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

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

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代码如下:

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写入数据ddr3writedata,置写入使能信号ddr3writeen为有效,并更新写入地址ddr3writeaddr。地址计数器writeaddr_counter在每次写入时递增。

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

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

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时置starttx为有效,开始将DDR3读取的数据ddr3readdata通过PCle发送出去,置pcietxvalid为有效表示PCle发送数据有效。

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

#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在现代高速数据处理应用中的魅力所在,灵活的逻辑设计能够满足各种不同的数据交互需求。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/31 15:08:52

Qwen3-VL-8B:轻量级多模态Embedding新选择

Qwen3-VL-8B&#xff1a;轻量级多模态Embedding新选择 在智能应用遍地开花的今天&#xff0c;用户早已不再满足于“输入文字、得到回复”的单一交互方式。他们希望系统能看懂截图里的报错信息、理解商品图与标题是否匹配、甚至仅凭一张照片就能推荐穿搭风格——这些能力背后&am…

作者头像 李华
网站建设 2026/1/31 18:11:54

本地部署EmotiVoice多音色情感TTS

本地部署 EmotiVoice 多音色情感TTS 在语音合成技术飞速发展的今天&#xff0c;我们早已不满足于“机器念字”式的冰冷朗读。无论是虚拟偶像的深情告白、游戏NPC的情绪爆发&#xff0c;还是智能助手的一句温柔晚安——人们期待的是有温度的声音。而EmotiVoice正是为此而生。 …

作者头像 李华
网站建设 2026/1/27 15:25:53

LobeChat能否实现代码注释生成?文档完整性保障工具

LobeChat能否实现代码注释生成&#xff1f;文档完整性保障工具 在现代软件开发节奏日益加快的今天&#xff0c;一个常见的尴尬场景是&#xff1a;功能刚上线&#xff0c;产品经理催着看效果&#xff0c;而新来的同事却对着一段没有注释的“天书代码”发呆。更糟的是&#xff0c…

作者头像 李华
网站建设 2026/1/23 17:23:00

12.16 脚本网页 Golang标准库

功能&#xff0c;查看go标准库&#xff0c;优点&#xff0c;单页面&#xff0c;便于集成APP<!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width,…

作者头像 李华
网站建设 2026/1/31 16:00:03

【LInux】进程程序替换与shell实现:从fork到exec的完整闭环

文章目录进程程序替换与shell实现&#xff1a;从fork到exec的完整闭环一、进程程序替换1.1 为什么需要程序替换1.1.1 shell如何执行命令1.2 程序替换的原理1.3 exec函数族详解1.3.1 命名规律1.3.2 六个函数的对比1.4 exec函数使用示例1.4.1 基本使用&#xff1a;execl1.4.2 使用…

作者头像 李华
网站建设 2026/1/26 13:05:53

Github 9.6k Star!一款开源的超强大数据可视化神器!

Perspective Perspective是一个交互式分析和数据可视化组件&#xff0c;特别适用于大型或流式数据集。使用它来创建用户可配置的报告、仪表板、笔记本和应用。 在Github上斩获9.6k Star&#xff01; 核心特性 极致性能体验 Perspective使用C编写&#xff0c;并编译为WebAss…

作者头像 李华