FPGA网络通信实战:基于CH395的UDP协议实现与调试
2026/6/6 12:37:11 网站建设 项目流程

1. 项目概述与UDP协议核心价值

搞FPGA开发,尤其是涉及到网络通信,很多朋友一上来就想搞TCP,觉得可靠、稳定。但实际在嵌入式、工业控制或者一些对实时性要求高、允许少量丢包的场景里,UDP(用户数据报协议)才是那个“快刀手”。它简单、高效,没有TCP那些复杂的握手、确认、重传和拥塞控制机制,数据包“即发即走”,延迟极低。这个实例,就是基于特权同学SF-CY3开发板,利用CH395以太网控制器芯片,实现FPGA与PC之间UDP数据收发的一个完整实战。通过它,你不仅能掌握如何在FPGA侧驱动以太网芯片进行UDP通信,更能深刻理解UDP协议帧的格式、端口的概念,以及在实际调试中如何抓包、分析数据,这是从理论到实践的关键一步。无论你是FPGA新手想切入网络应用,还是已经做过一些简单通信想深入协议层,这个实例都能给你提供一套清晰、可复现的“操作手册”。

2. UDP协议深度解析与CH395工作模式设定

2.1 UDP协议帧结构:从IP包到数据报

很多人觉得UDP简单,就是“IP包+端口号”,但真要自己动手组包解包,细节决定成败。UDP报文是封装在IP数据报中的,其整体结构可以这样理解:一个完整的以太网帧,包含了以太网头部、IP头部、UDP头部,最后才是你的应用数据。

IP头部:其中有一个8位的“协议”字段,这个字段指明了IP数据报内承载的上层协议类型。对于UDP,这个值固定为0x11(十进制17)。这是网络栈进行协议分用的关键标识,意味着IP层在解包后,会把数据部分交给UDP协议处理。

UDP头部:紧跟在IP头部之后,长度固定为8字节,包含四个字段:

  1. 源端口号(16位):发送方应用程序使用的端口。
  2. 目的端口号(16位):接收方应用程序监听的端口。
  3. 长度(16位):指UDP头部和UDP数据的总长度,以字节为单位。最小值是8(即只有头部,没有数据)。
  4. 校验和(16位):用于校验UDP头部和数据在传输过程中是否出错。这里有个关键点:UDP的校验和计算是可选的。如果发送方不计算(置为0),接收方检测到校验和有误也不会产生错误报告,直接丢弃。但在实际可靠应用中,强烈建议启用。

UDP数据:这就是你的实际应用数据,长度可变。但受限于下层协议,一个UDP数据报的最大长度理论上为65535字节减去IP和UDP头部的开销。在实际的以太网环境中,还要考虑MTU(最大传输单元,通常1500字节),因此UDP数据部分的长度最好不超过1472字节(1500 - 20字节IP头 - 8字节UDP头),以避免IP分片,影响效率和增加丢包风险。

注意:UDP的“长度”字段其实是有冗余的,因为IP头部的“总长度”字段已经包含了整个IP数据报的长度。但UDP保留这个字段,更多是从协议分层和独立性的角度考虑。

2.2 CH395芯片的UDP模式配置要点

CH395是一款集成MAC和PHY的以太网控制器,通过并口或SPI与主控制器(如FPGA)通信,大大简化了硬件设计。将其配置为UDP模式,是本次实验的核心。

配置流程关键步骤:

  1. 初始化与Socket创建:首先需要通过命令初始化CH395,并创建一个Socket。CH395支持多个Socket,本例我们使用Socket 0。
  2. 设置协议类型:发送命令CMD_SET_PROTO_TYPE_SN,并指定参数为PROTO_TYPE_UDP,告诉芯片这个Socket用于UDP通信。
  3. 设置本地(源)端口:发送命令CMD_SET_SOUR_PORT_SN,参数为你希望FPGA端监听的端口号,例如5000。这意味着FPGA将监听来自网络、目的端口为5000的UDP包。
  4. 设置目标(目的)IP与端口:对于UDP,它是无连接的,但CH395在发送时需要一个默认的目标地址。使用命令CMD_SET_IP_ADDR_SN设置目标IP(如PC的IP 192.168.1.103),使用CMD_SET_DES_PORT_SN设置目标端口(如8000)。这样,当FPGA通过这个Socket发送数据时,数据包就会发往这个指定的IP和端口。
  5. 打开Socket:最后发送CMD_OPEN_SOCKET_SN命令,启动Socket。此时,CH395开始监听指定的本地端口,并准备发送数据。

中断处理逻辑:CH395通过中断引脚通知FPGA状态变化,FPGA需要查询中断状态寄存器。

  • 发送相关SINT_STAT_SEND_OK表示一个UDP包已成功发送到网络。SINT_STAT_SENBUF_FREE表示发送缓冲区有空闲,可以写入新的待发送数据。这里有个重要限制:CH395的单个Socket发送缓冲区通常为2KB,因此FPGA单次通过CMD_WRITE_SEND_SN命令写入的数据长度不能超过2KB。如果应用数据超过1472字节,CH395会自动将其拆分成多个符合MTU的UDP包依次发送,每成功发送一个都会产生SINT_STAT_SEND_OK中断。
  • 接收相关SINT_STAT_RECV是最重要的接收中断,表示Socket接收缓冲区有数据到达。FPGA需要立即响应。

2.3 端口号:网络通信的“门牌号”与“集装箱码头”

端口的概念对于理解网络编程至关重要。你可以把IP地址想象成一栋大楼(主机),而端口号就是这栋大楼里一个个房间的门牌号。一个IP地址(大楼)可以提供多种服务(多个房间),如Web服务(80端口)、FTP服务(21端口),它们通过不同的端口来区分。

端口号范围:16位无符号整数,范围0-65535。

  • 0-1023:公认端口,由IANA分配,给系统级或知名服务使用(如HTTP:80, HTTPS:443, FTP:21)。你的应用程序应避免使用这些端口,除非你就是在实现这些服务。
  • 1024-49151:注册端口,用于用户级的常见应用程序,也可以由普通程序使用。
  • 49152-65535:动态/私有端口,通常用于客户端的临时端口,或内部测试。

在UDP通信中,通信双方需要两个信息来唯一标识一次通信:源IP:源端口目的IP:目的端口。这四元组共同定义了一个UDP数据流的通道。在本次实验中,我们固定了FPGA端的IP和端口(192.168.1.101:5000),以及PC端的IP和端口(192.168.1.103:8000)。数据从PC发往FPGA时,源是PC:8000,目的是FPGA:5000。FPGA回发时,源就变成了FPGA:5000,目的是PC:8000。网络设备(包括CH395)正是根据目的IP和目的端口来将数据包递交给正确的应用程序(Socket)。

3. 软件设计与FPGA逻辑实现详解

3.1 系统软件流程与状态机设计

基于Nios II软核或纯FPGA状态机,控制CH395完成UDP通信需要一个清晰的状态流程。下图描绘了核心的状态迁移:

(此处应为文字描述状态机,因禁止Mermaid图表)状态机文字描述:

  1. 初始化状态 (INIT):上电或复位后进入。依次发送CH395硬件复位命令、通用初始化命令,等待芯片就绪。
  2. Socket配置状态 (CFG_SOCKET):初始化成功后,进入此状态。按顺序发送设置协议类型(UDP)、设置本地端口、设置目标IP、设置目标端口等命令。
  3. 打开Socket状态 (OPEN_SOCKET):配置完成后,发送打开Socket命令,并等待打开成功的中断或响应。
  4. 空闲监听状态 (IDLE):Socket成功打开后,进入主循环空闲状态。在此状态下,FPGA持续轮询或等待CH395的中断信号。
  5. 中断处理状态 (INT_HANDLE):当检测到中断引脚有效,跳转到此状态。读取中断状态寄存器,判断中断类型。
  6. 接收数据处理子状态 (PROC_RECV):如果是SINT_STAT_RECV中断,则: a. 发送CMD_GET_RECV_LEN_SN命令,获取接收缓冲区数据长度。 b. 根据长度,循环发送CMD_READ_RECV_SN命令,读取数据到FPGA内部的缓冲区(如RAM)。 c. 数据读取完毕后,可选项:发送CMD_CLEAR_RECV_SN命令清空缓冲区,准备接收新数据。 d. 将读取到的数据存入处理队列,并触发发送响应状态
  7. 发送响应状态 (SEND_RESPONSE):从接收数据处理状态跳转而来。将接收到的数据按字节取反(本例逻辑),或进行其他应用层处理。 a. 检查发送缓冲区是否空闲(可通过上次发送完成的标志,或主动查询)。 b. 发送CMD_WRITE_SEND_SN命令,将处理后的数据写入CH395发送缓冲区。注意单次写入长度不超过2KB。 c. 写入完成后,CH395会自动将数据封装成UDP包发出(若数据长,会分片)。
  8. 发送完成等待状态 (WAIT_SEND_OK):写入数据后,等待SINT_STAT_SEND_OK中断,确认数据包已成功送入网络。收到此中断后,清除发送忙标志,返回空闲监听状态
  9. 发送缓冲区空闲处理 (PROC_SENBUF_FREE):如果处理的是SINT_STAT_SENBUF_FREE中断,表示之前可能满的发送缓冲区现在有空闲了。此时可以解除FPGA侧的发送阻塞标志,允许下一次数据写入。

这个状态机确保了操作的顺序性和可靠性,避免了命令冲突和数据覆盖。

3.2 FPGA与CH395的硬件接口与驱动编写

CH395通常提供并行接口或SPI接口。SF-CY3开发板 likely 使用的是并行接口,因为它速度更快,适合FPGA直接操作。

并行接口关键信号:

  • DATA[7:0]: 8位双向数据总线,用于命令、数据和状态的传输。
  • A0: 地址/数据选择线。当A0=1时,数据总线上传输的是命令字;当A0=0时,传输的是命令参数或数据。
  • CS_N: 片选信号,低有效。
  • WR_N/RD_N: 写和读选通信号,低有效。
  • INT: 中断输出,低电平有效,通知FPGA有事件需要处理。

驱动编写要点(以Verilog为例):

  1. 命令写入函数:需要严格按照CH395的时序要求。先拉低CS_N,设置A0=1,将命令码放到DATA总线上,产生一个WR_N的负脉冲。如果需要写入命令参数,则紧接着设置A0=0,将参数放到DATA总线上,再产生WR_N负脉冲。多个参数就重复此步骤。
    // 伪代码示例:写入一个带一个参数的命令 task write_ch395_cmd; input [7:0] cmd; input [7:0] param; begin cs_n <= 1‘b0; a0 <= 1’b1; data_out <= cmd; #10 wr_n <= 1‘b0; // 产生写脉冲 #30 wr_n <= 1’b1; #10 a0 <= 1‘b0; data_out <= param; #10 wr_n <= 1’b0; #30 wr_n <= 1‘b1; #10 cs_n <= 1’b1; data_out <= 8‘hz; // 释放总线 end endtask
  2. 数据读取函数:用于读取接收到的数据或状态。设置A0=0,拉低RD_N,从DATA总线读入数据。
  3. 中断处理模块:需要有一个边沿检测电路检测INT引脚的下跳沿。一旦检测到中断,主状态机应跳转到中断处理状态,发送CMD_GET_INT_STATUS命令获取详细的中断状态字节,再根据位判断是哪个Socket的何种中断。

实操心得:CH395的并行接口时序要求并不苛刻,FPGA的时钟频率远高于其要求。关键是要保证信号之间的建立和保持时间。在编写驱动时,建议先用一个简单的测试,比如连续读取芯片版本号,验证底层读写时序的正确性,再往上搭建协议栈。另外,INT中断线是共享的,读取中断状态后,需要发送CMD_CLR_INT命令来清除已处理的中断标志,否则中断线会一直保持有效。

3.3 数据缓冲区管理与流控策略

在UDP模式下,流控完全由应用层负责。CH395的接收缓冲区是有限的(通常每个Socket 2KB或更多)。如果FPGA处理速度跟不上数据包到达的速度,缓冲区就会溢出,导致丢包。

FPGA侧缓冲区设计建议:

  1. 双缓冲(Ping-Pong Buffer):在FPGA内部用两块RAM作为应用层接收缓冲区。当一块缓冲区正在被CH395的数据填充(或等待处理)时,另一块缓冲区可以用于处理之前接收到的数据(如取反、存储、转发)。通过状态标志位切换,实现无缝衔接,提高吞吐率。
  2. 及时读取:一旦进入SINT_STAT_RECV中断处理流程,应尽快将数据从CH395的Socket缓冲区读出,释放空间。即使应用层一时处理不完,也应先读到FPGA内部更大的缓冲区中。
  3. 长度检查:在CMD_READ_RECV_SN之前,务必先使用CMD_GET_RECV_LEN_SN获取准确的数据长度。不要假设每次收到的都是固定长度。

发送侧流控:虽然UDP本身无流控,但FPGA与CH395之间需要协调。在写入发送数据前,最好检查一个“发送允许”标志。这个标志在SINT_STAT_SENBUF_FREE中断到来时置位,在开始写入发送数据时清零。这样可以避免在CH395发送缓冲区满时写入造成数据丢失或错误。

4. 板级调试实战与网络数据分析

4.1 使用科来网络分析系统进行协议抓包与验证

科来网络分析系统(或Wireshark)是网络开发的“显微镜”,它能让你看到每一个在网络中穿梭的比特。调试UDP通信,离不开它。

抓包设置与关键过滤条件:

  1. 选择正确的网卡:确保抓取的是连接FPGA开发板的那个物理网卡或虚拟网卡的流量。
  2. 设置过滤条件:为了清晰看到实验相关的包,可以使用过滤表达式ip.addr == 192.168.1.101 && udp。这表示只显示源或目的IP是FPGA(192.168.1.101)的UDP包。
  3. 发送测试包:按照实例描述,在科来的“数据包生成器”中构造一个UDP包。
    • 二层(以太网):源MAC填PC的,目的MAC填FPGA开发板的(或网关的,如果不在同一网段)。
    • 三层(IP):源IP 192.168.1.103,目的IP 192.168.1.101,协议类型17(UDP)。
    • 四层(UDP):源端口8000(PC端),目的端口5000(FPGA端)。长度字段填108(8字节头+100字节数据)。校验和可以置0(不计算)或由工具自动计算。
    • 数据:填充100字节的测试数据,例如从0x00递增到0x63。

分析抓取到的包:发送后,你应该在抓包界面看到两个包:

  1. 请求包:源192.168.1.103:8000-> 目的192.168.1.101:5000。展开这个包的UDP层,核对端口、长度、校验和。展开数据部分,应为你发送的100字节原始数据。
  2. 响应包:源192.168.1.101:5000-> 目的192.168.1.103:8000。这个包是FPGA回发的。重点检查其UDP数据部分,应该是对请求包数据逐字节取反的结果。例如,请求数据是0x00,响应就应该是0xFF;请求是0x55,响应就是0xAA。

通过对比这两个包,你可以100%确认FPGA的逻辑完全正确:正确接收、正确处理(取反)、正确发送。任何一步出错,包的内容或流向都会不对。

4.2 基于LabVIEW UDP调试助手的应用层测试

科来是底层协议测试利器,而LabVIEW UDP调试助手则模拟了一个真实的上位机应用程序,更贴近实际应用场景。

配置与调试步骤:

  1. 远程主机设置:“远程主机IP”填FPGA的IP(192.168.1.101),“远程主机端口”填FPGA监听的端口(5000)。这定义了数据发往哪里。
  2. 本地设置:“本地端口”填PC端应用程序绑定的端口(8000)。FPGA回发数据时,目的端口就是8000,所以这个端口必须打开并监听。
  3. 数据格式:务必设置为“十六进制”,这样输入和显示的都是原始的字节数据,便于观察取反操作。
  4. 发送与接收:在发送框输入一串十六进制数,点击发送。在接收框,你应该立即看到一串新的十六进制数,它就是发送数据的逐字节取反。

避坑技巧:很多人在使用这类工具时,发现发送后收不到回复。请按以下顺序排查:① 确认FPGA程序已运行,Console打印了初始化成功信息。② 确认PC端防火墙没有阻止UDP 8000端口(可暂时关闭防火墙测试)。③ 确认网络是连通的(ping一下FPGA的IP)。④ 用科来抓包,看请求包是否真的从PC的8000端口发出了,以及FPGA是否有响应包从5000端口发回。如果请求包发出但无响应,问题在FPGA程序;如果请求包都没发出,问题在PC端工具或网络配置。

4.3 Nios II Console信息解读与调试辅助

在EDS(Embedded Design Suite)或Quartus的Nios II Console中,我们可以让FPGA程序打印丰富的调试信息。

典型的打印信息流:

CH395 Version: 0x39 // CH395芯片版本号,验证通信正常 Socket 0 opened in UDP mode. // Socket 0以UDP模式成功打开 Local IP: 192.168.1.101, Port: 5000 // 本地IP和端口 Remote IP: 192.168.1.103, Port: 8000 // 目标IP和端口 Waiting for UDP packet... // 进入主循环,等待数据 [INT] RECV interrupt received. // 收到接收中断 [RECV] Length: 100 bytes. // 获取到接收数据长度为100字节 [RECV] Data: 00 01 02 ... 63 // 打印接收到的原始数据(十六进制) [SEND] Data inverted and sent back. // 数据已取反并发送 [INT] SEND_OK interrupt received. // 发送完成中断确认

这些信息是调试的生命线。如果卡在“Waiting for UDP packet...”不动,说明没收到包,检查网络和PC端发送工具。如果收到了RECV中断但长度不对或数据乱码,可能是FPGA读取CH395缓冲区的时序或逻辑有误。如果收到了SEND_OK中断但PC端没收到回包,用科来抓包看响应包是否真的从网口发出了。

5. 常见问题深度排查与性能优化技巧

5.1 典型问题排查速查表

问题现象可能原因排查步骤与解决方法
FPGA无法初始化CH3951. 硬件连接错误(线序、虚焊)
2. 电源或时钟不正常
3. FPGA驱动时序不符合CH395要求
1. 用万用表检查电源、复位信号。
2. 用示波器检查晶振是否起振,时钟频率是否正确。
3. 编写最简单的读写测试(如读版本号),用逻辑分析仪抓取CS_N, A0, WR_N, RD_N, DATA的波形,与CH395数据手册的时序图严格对比。重点检查建立时间和保持时间。
能初始化,但Socket打不开1. 网络电缆未连接或损坏
2. PHY自协商失败
3. 配置命令序列错误或参数错误
1. 检查网线、网口指示灯(Link灯是否常亮,Act灯是否闪烁)。
2. 尝试强制设置CH395的PHY工作模式(如100M全双工),而非自动协商。
3. 在发送打开Socket命令后,读取Socket状态寄存器,确认是否返回成功。检查之前的设置IP、端口命令是否都执行成功。
PC发送数据,FPGA无反应(无RECV中断)1. 目的IP或目的端口设置错误
2. FPGA本地端口监听错误
3. 网络中存在防火墙/路由器阻拦
4. UDP校验和错误且被静默丢弃
1. 用科来抓包,确认PC发出的UDP包目的IP和端口是否是FPGA设置的(192.168.1.101:5000)。
2. 确认FPGA程序正确设置了本地端口并成功打开Socket。
3. 确保PC和FPGA在同一子网,且无防火墙规则阻止UDP 5000端口。可暂时关闭PC防火墙测试。
4. 尝试在PC端构造UDP包时,将校验和字段设为0(不启用),看FPGA是否能收到。
FPGA收到数据但回发后PC收不到1. FPGA发送的目标IP/端口错误
2. FPGA发送缓冲区管理错误,数据未真正发出
3. PC端未监听相应端口(8000)
1. 用科来抓包,看是否有从192.168.1.101:5000发往192.168.1.103:8000的包。如果没有,检查FPGA发送流程。
2. 确认FPGA在写入发送数据后,是否等待并收到了SINT_STAT_SEND_OK中断。没有这个中断,可能发送未完成。
3. 确认PC端的UDP调试助手绑定了8000端口,并且没有其他程序占用该端口。
通信不稳定,偶尔丢包1. FPGA处理速度慢,CH395接收缓冲区溢出
2. 网络拥塞(在复杂网络环境中)
3. 中断处理不及时,丢失中断
1. 优化FPGA代码,提高中断响应速度和数据搬运效率。考虑使用DMA或更高效的总线。
2. 对于UDP,应用层应增加简单的序列号和确认重传机制,如果业务要求可靠。
3. 检查FPGA中断处理例程是否过长时间关闭中断,导致后续中断丢失。确保中断服务程序尽量短小精悍。
数据内容错误1. FPGA读取/写入CH395缓冲区时,字节序或位序错误
2. 数据处理逻辑(如取反)有bug
3. 发送和接收的数据长度不一致
1. 逐字节比对。用科来抓取PC发送的原始包,与FPGA Console打印的接收数据比对。再用FPGA发送的数据与科来抓取的响应包比对。定位错误发生在接收、处理、发送的哪个环节。
2. 检查取反操作是按字节(8bit)进行,而不是按32位字进行。
3. 确保CMD_READ_RECV_SN读取的字节数与CMD_GET_RECV_LEN_SN获取的长度一致。

5.2 性能优化与进阶应用思考

在基本功能调通后,可以考虑以下优化和扩展方向:

1. 提升吞吐率:

  • 使用DMA:如果FPGA资源充足,可以设计一个DMA控制器,代替CPU(Nios II)或状态机来搬运CH395缓冲区与FPGA内部RAM之间的数据。这能极大解放CPU,减少中断延迟,提高大数据量传输的效率。
  • Socket缓冲区管理:CH395可能支持设置更大的Socket缓冲区。查阅数据手册,看是否可以通过命令配置接收/发送缓冲区大小,以适应更高带宽的应用。
  • 批量操作:在读取接收数据或写入发送数据时,尽量使用连续读写命令,减少命令交互的开销。

2. 增加应用层可靠性:UDP本身不可靠,但在许多工业场合,我们既需要UDP的低延迟,又需要一定的可靠性。可以在应用层实现简单的可靠传输机制:

  • 添加帧头:在UDP数据载荷前添加自定义帧头,包含序列号确认号标志位(如ACK)等。
  • 实现确认重传:接收方收到数据后,解析序列号,并回发一个包含确认号的UDP包。发送方启动定时器,如果在规定时间内没收到确认,则重传该序列号的数据包。
  • 注意:这种简易重传会增加延迟,且需要处理乱序到达,适用于对丢包敏感但延迟要求不那么极致的场景。对于高速实时系统,可能更需要前向纠错(FEC)码。

3. 多Socket与并发处理:CH395支持多个独立的Socket。你可以让Socket 0处理来自某个端口的数据采集,Socket 1处理向另一个服务器的命令发送。FPGA需要维护每个Socket的状态(打开、监听、发送中、接收中等),并正确路由中断到对应的处理流程。这要求你的状态机设计更加模块化和清晰。

4. 移植到纯逻辑(无软核):本例基于Nios II软核,代码用C编写,易于理解。但对于追求极致性能、低功耗或需要更多逻辑资源的项目,可以将整个协议栈用Verilog/VHDL实现。这包括:

  • 用硬件状态机实现CH395命令序列发生器。
  • 用FIFO或双端口RAM实现数据缓冲区。
  • 用硬件逻辑实现UDP/IP帧的组装(计算校验和、填充长度字段)与解析。 这是一个更大的挑战,但能让你对网络协议有更透彻的理解,并实现纳秒级的协议处理速度。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询