参考资料:【正点原子】开拓者之FPGA开发指南 V3.2

一.协议栈

1.1 最小可用小协议栈(mini stack)

在网线上真正跑的是 以太网帧(Ethernet Frame)。UDP 只是帧里面的“内容的一部分”。

手册也用“逐层封装”的说法讲得很明确:
MAC 帧的数据段是 IP 数据报;IP 的数据段是 UDP 报文;UDP 的数据段才是用户数据。

所以做“以太网 UDP 数据报文发送电路”,本质是在 FPGA 里实现一个“最小可用小协议栈(mini stack)”:

做一个 “能和电脑互通的最小子集”

  • L2(链路层):会收发以太网帧 + 生成正确 CRC32/FCS(不然网卡丢包)

  • ARP:至少能让电脑通过 ARP 得到你的 MAC(不然电脑发不了 IP/UDP 给你)

  • L3 IPv4(网络层):能解析/生成 IP 头 + 校验和(不然对方丢 IP 包)

  • L4 UDP(传输层):能正确组/拆 UDP 头和数据(校验和可先不做,后面再补)

1.2 ARP/ICMP 必要性

1)为什么 UDP 之前一定要先有 ARP

你在以太网上真正发出去的是 以太网帧(Ethernet Frame),帧头里必须有 目的 MAC 地址

  • 上位机应用(比如你写 UDP 调试助手)通常只知道“对方 IP”,但网卡转发帧必须靠“目的 MAC”。

  • ARP 的作用就是:根据目的 IP 自动获取目的 MAC,并把结果缓存起来(ARP 缓存表/ARP 映射),下次就不用再查了。手册也明确写了:ARP 是“根据 IP 获取 MAC”,且缓存会过期,静态绑定维护很麻烦。

所以你想做 UDP 工程时,如果没有 ARP,就只能:

  • 方案 A:静态写死 MAC(很不通用,换电脑/换网卡 MAC 就变了)

  • 方案 B:手动在电脑里静态绑 ARP 表(更麻烦,维护成本高)

结论:做 UDP 通信时,ARP 基本是“必选地基”。

2)为什么会在 UDP 前先做 ICMP(ping)

严格讲:UDP 不一定“必须”实现 ICMP 才能收发 UDP。

但教程一般会先做 ICMP 的原因很现实——ping 是最便宜、最直观的“网络连通性自检”

  • ping 的请求/应答就是 ICMP Echo(ICMP Echo请求与应答):Type=0x08 请求,Type=0x00 应答,并且要匹配序列号等字段。

  • 手册对 ICMP 实验任务写得很直白:电脑 ping FPGA,FPGA 收到后再回给电脑

  • 更关键的是:电脑在 ping 之前也会先 ARP。所以 ICMP 实验天然把“ARP + IP + ICMP”这条链路都验证了一遍:

    • ARP 不通 → ping 根本发不到 FPGA

    • IP 头/校验不对 → ping 会失败

    • ICMP 组包不对 → 也失败
      这对你后续做 UDP 是非常好的“保命测试”。

另外,手册也说明了:ICMP 相比 ARP 工程,替换了控制模块并增加了 FIFO 和 ICMP 顶层。

这一步就是在为后续 UDP 的“多协议切换/复用”打基础。

1.3 把“协议栈”落到 FPGA 工程思路上(整体架构)

二 ARP 实验任务:

  • 电脑发 ARP Request → FPGA 回 ARP Reply

  • 按下开发板触摸按键 TPAD → FPGA 主动发 ARP Request → 电脑回 ARP Reply

结构:

三 顶层 eth_arp_test.v

3.1 端口:RGMII 物理口 + 复位 + 触摸按键

  • eth_rxc / eth_rx_ctl / eth_rxd[3:0]:RGMII 接收

  • eth_txc / eth_tx_ctl / eth_txd[3:0]:RGMII 发送

  • sys_rst_n:系统复位

  • touch_key:触摸按键(触发 FPGA 主动 ARP 请求)

  • eth_rst_n:给以太网芯片的复位脚(顶层直接 assign eth_rst_n = sys_rst_n;

3.2 四个关键参数(决定你电脑怎么配 IP)

顶层写死了:

  • 开发板 MAC:00-11-22-33-44-55

  • 开发板 IP:192.168.1.10

  • 默认目的 MAC:广播 ff:ff:ff:ff:ff:ff(用于 ARP Request)

  • 默认目的 IP:192.168.1.102(一般设成你电脑 IP)

这和手册后面验证用的 IP 是一致的(比如 ping 192.168.1.10

3.3 PLL:“相位偏移 eth_rxc”

顶层例化:

  • pll u_pll(.inclk0(eth_rxc), .c0(eth_rxc_deg))

含义:把 RGMII 接收时钟 eth_rxc 做相位偏移,再送进 gmii_to_rgmiirgmii_rxc
目的很实际:RGMII 是 DDR(上下沿采样),为了采样更稳,常会把采样时钟挪到数据眼图中间(工程里就叫 eth_rxc_deg)。

3.4 gmii_to_rgmiiDDR ↔ SDR 转换

  • RGMII:4bit DDR(上升沿+下降沿各 4bit)

  • GMII:8bit SDR(每个时钟上升沿 8bit)

“翻译器”:

  • PHY 过来的是 RGMII:eth_rxd[3:0] + eth_rx_ctl + eth_rxc

  • 转成内部更好处理的 GMII:gmii_rxd[7:0] + gmii_rx_dv + gmii_rx_clk

  • 发包时反过来:gmii_txd/gmii_tx_eneth_txd/eth_tx_ctl

gmii_to_rgmii 内部就是:

  • rgmii_rx:用 ALTDDIO_IN 把 DDR “拆成” 上升沿数据 dataout_h 和下降沿数据 dataout_l

    手册还给了实例:ddi_x4rgmii_rxd[3:0] 转成 gmii_rxd[7:0]ddi_x1rgmii_rx_ctl 转成 gmii_rx_dv
  • rgmii_tx:用 ALTDDIO_OUT 把 GMII 的 8bit SDR “合成” 4bit DDR 输出到 RGMII(手册说这块用 ALTDDIO_OUT)
     

3.5 arp + arp_ctrl:谁负责协议,谁负责“什么时候发”

  • arp:负责解析 ARP、组 ARP 帧、发 Request/Reply。手册明确:ARP 顶层 = arp_rx + arp_tx + crc32_d8

  • arp_ctrl:负责“按键触发 / 收到对方 ARP 后触发应答”等控制流程

顶层还有一行非常关键:

  • assign des_mac = src_mac;

  • assign des_ip = src_ip;

意思:收到谁的 ARP(对方 MAC/IP)就把它当成下一次发送的目的 MAC/IP
所以你按 TPAD 主动发 ARP Request 时,目标就是“最近一次学到的对端”(或默认目的 IP/MAC 配合控制逻辑)。

四 下载后怎么验证

4.1 先让 FPGA 主动发 ARP(按 TPAD)

你按 TPAD 后,电脑 ARP 缓存表里会出现开发板的 IP/MAC 映射。手册也提示失败就检查:

  • 网线是否连接开发板 PL 网口(GE_PL)

  • 是否按下 TPAD 触发

  • 电脑网口是否支持千兆(不支持可能失败)

4.2 再验证“电脑发 ARP → FPGA 回应答”(用 ping 间接触发 ARP)

手册给的步骤是:

  1. 先把 ARP 缓存删掉:arp -d(最好管理员运行,否则可能删不掉)

  2. ping 192.168.1.10 让电脑发起 ARP 请求

    手册提醒:因为这个实验没做 ICMP,所以 ping 会超时,但 ARP 过程会发生,开发板会回 ARP Reply
  3. arp -a 查看是否学到了开发板 MAC

4.3 用 Wireshark 抓包(看到“真实数据包”你会瞬间通透)

手册步骤:

  • 打开 Wireshark,选网卡开始抓包

  • 然后按下 TPAD,就能在 Wireshark 里抓到开发板发出的包

五 路线 B:新建工程 + 自己生成 IP 核 + 自己约束

准备工作:

硬件:FPGA开发板(我的是正点原子开拓者FPGA),

PTD03下载器,

千兆USB转网口(有的笔记本没有网口),

千兆网线

B1. 新建 Quartus 工程

  1. New Project Wizard → 选择工程目录→ 选芯片型号

B2. 添加源码

这里不自己编写源码了,用原子的ARP源码

开拓者FPGA开发板EP4CE10 — 正点原子资料下载中心 1.0.0 文档

加进工程:

  • rtl/eth_arp_test.v,arp_ctrl.v

  • rtl/arp/*.v

  • rtl/gmii_to_rgmii/*.v

B3. 生成/加入 IP 核

B3.1 这个工程里核心 IP 主要是两类:

① DDR 采样/输出用的 ALTDDIO

  • ALTDDIO_IN(手册给了生成思路:RGMII 是 4 位输入所以 Width=4;也会用 1 位版本给 rx_ctl

    手册还解释了关键端口含义:datain/inclock/dataout_h/dataout_l
  • ALTDDIO_OUT:用于发送侧 SDR→DDR(手册说 rgmii_tx 用 ALTDDIO_OUT)

你需要生成四个变体(工程里也叫这些名字):

  • ddi_x4:4bit 输入 DDR

  • ddi_x1:1bit 输入 DDR

  • ddo_x4:4bit 输出 DDR

  • ddo_x1:1bit 输出 DDR

说明:

关于RGMII,MII接口不再梳理,之前文章学习过,这里直接用IP核

FPGA学习笔记(5)数字接口时序设计-CSDN博客

② PLL(给 eth_rxc 做相位偏移)

  • 顶层例化了 pll,输入 eth_rxc,输出 eth_rxc_deg(相位偏移后的时钟)

  • IP 核就是“厂家已经实现好的硬件模块生成器”,你在 Quartus 里点几下配置,它就会吐出 .v/.qip 等文件给你例化用。

B 3.2 生成5 个 IP 核(4 个 DDIO + 1 个 PLL)

0)先准备:建一个 ipcore 文件夹

在你新工程目录下建一个文件夹,比如:

  • ...\your_project\ipcore\

后面所有 IP 都生成到这里,文件不会乱。


1)生成 DDR 输入 IP:ddi_x4(ALTDDIO_IN,宽度4)

用途:RGMII 的 rxd[3:0] DDR 采样成 dataout_h/dataout_l

① 打开向导
  1. Quartus 顶部菜单:Tools → ip catalog

② 选 IP 类型
  1. ALTDDIO → ALTDDIO_IN

  2. 选中 ALTDDIO_IN → Next

③ 设置输出文件名 & 语言
  1. “Name” 填:ddi_x4

  2. “Directory” 选你刚建的:...\ipcore\

  3. HDL 语言选:Verilog HDL

  4. Next

④ 关键参数(Width)
  1. 找到 Width(数据位宽)设为:4

  2. 其余选项保持默认(不要启用 reset/enable/oe 等)

  3. Next → Finish(或一直 Next 到 Finish)

  4. 弹出窗口-确定加入工程

⑤ 生成完你应该看到这些文件(正常现象)

ipcore 文件夹里会出现:

  • ddi_x4.v

  • ddi_x4.qip

  • 以及一些 .cmp/.inc/.bsf/.ppf 等(可有可无)

说明:

很关键的“不要勾”的点(保证端口匹配源码)

向导里如果出现这些选项:

  • Output enable (oe) / Use oe:不要勾

  • Reset (aclr/aset/sclr/sset):不要勾

  • Clock enable:不要勾

因为源码里例化 ddo_x4 只连了这四个端口:
datain_h, datain_l, outclock, dataout
如果你勾了 oe/reset,生成的模块端口会变多,源码就对不上了。

2)生成 DDR 输入 IP:ddi_x1(ALTDDIO_IN,宽度1)

用途:RGMII 的 rx_ctl 也是 DDR(1bit)

完全重复上面步骤,只改两处:

  • Name:ddi_x1

  • Width:1


3)生成 DDR 输出 IP:ddo_x4(ALTDDIO_OUT,宽度4)

用途:把 GMII txd[7:0] 拆成 datain_h[3:0]datain_l[3:0] 输出到 RGMII txd[3:0]

4)生成 DDR 输出 IP:ddo_x1(ALTDDIO_OUT,宽度1)

用途:输出 tx_ctl,以及用它“拼”一个 rgmii_txc(源码里 ddo_x1_clk

重复第3步,只改:

  • Name:ddo_x1

  • Width:1

5)生成 PLL:pll(ALTPLL)

用途:eth_rxc → 生成相位偏移的 eth_rxc_deg 给接收采样更稳

这里不自己生成了,因为原子手册里也没在本节介绍125MHZ pll生成步骤,但是前面有专门章节IP核之PLL实验,这里偷下懒不想复习自己整了

B4. 管脚/IO 约束(非常关键)

示例工程里给了 doc/eth_arp_test.tcl(里面是 pin 绑定 + IO_STANDARD)。你可以:

  • Quartus 里导入 tcl(或把内容复制到 .qsf

  • 或者 Pin Planner 手动填引脚

这一步决定:

  • eth_rxc / eth_rxd[3:0] / eth_rx_ctl / eth_txc / eth_txd[3:0] / eth_tx_ctl / eth_rst_n / sys_rst_n / touch_key 到底接到 FPGA 哪些管脚

说明:

输入、输出端口进行管脚分配,参考原理图来对引脚进行分配。三种方法,

Pin Planner界面,TCL文件,或把内容复制到 .qsf

这里用原子doc文档里TCL文件,运行TCL文件自动分配引脚

运行

B5. 编译 + 下载

  1. Compile 全流程跑完

  2. Programmer 选择硬件下载器(USB-Blaster)

  3. .sof 下载到板子

B6.验证现象(最短路径)

  1. 电脑网卡设置静态 IP:192.168.1.102,掩码 255.255.255.0

  2. 网线连开发板 GE_PL

  3. CMD:

    • arp -d *

    • ping 192.168.1.10(ping 可能超时没关系)

    • arp -a 看是否出现 192.168.1.10 -> 00-11-22-33-44-55

  4. Wireshark:过滤器输入 arp,按板子 TPAD,看是否有 ARP Request/Reply

0)连线下载


将下载器一端连接电脑,另一端与开发板上的JTAG下载口连接,将网线一端连接开发板的网口,另 一端连接电脑的网口,接下来连接电源线,并打开开发板的电源开关。下载

1)判断硬件

但是我的电脑下载成功但电脑没反应,排查:

发现是原因Windows 有时不会像手册截图那样明显弹“未识别网络”,

1)把电脑以太网改成静态 IP(否则后面 ARP 根本不一定发得对)

按工程默认参数,你电脑应设:

  • IP:192.168.1.102

  • 子网掩码:255.255.255.0

  • 网关:先留空都行

手册明确强调:必须手动设置电脑 IP,不能自动获取,并且就是设成 192.168.1.102

这里忘记截图了,拿手册上的吧,以太网右键-属性-设置:

2)用命令验证

以管理员打开 CMD(手册也强调要管理员)。管理员身份运行应该不能快捷键了,

验证说明直接截图手册了,说的很清楚

任务一:电脑发 ARP Request → FPGA 回 ARP Reply

任务二:按下开发板触摸按键 TPAD → FPGA 主动发 ARP Request → 电脑回 ARP Reply

说明:ping是一个十分强大的TCP/IP工具,它可以用来检测网络的连通情况和分析网络速度。ping命令是一个固定格式的ICMP(Internet控制报文协议)请求数据包,之后会发起ARP请求命令,所以这里是通过ping命令来间接发起ARP请求。由于开发板并没有实现ICMP协议,因此在ping时会请求超时,但是在ping的过程中发起的ARP请求,开发板会响应并返回ARP应答数据。

我的验证:

说明,跟手册有点验证过程不一样是,我刚开始电脑没反应时候就按触摸按键了,然后开发板发ARP请求了,电脑获取MAC了,所有我的一开始查看ARP缓存表就有开发板MAC和IP.

3)Wireshark(以太网通信时经常使用的抓包软件):

按板子 TPAD,看是否有 ARP Request/Reply

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐