一,简述以太网
以太网简介
以太网是一种计算机局域网技术。IEEE组织的IEEE 802.3标准制定了以太网的技术标准,它规定了包括物理层的连线、电子信号和介质访问层协议的内容。
以太网类型介绍
以太网是现实世界中最普遍的一种计算机网络。以太网有两类:第一类是经典以太网,第二类是交换式以太网,使用了一种称为交换机的设备连接不同的计算机。经典以太网是以太网的原始形式,运行速度从3~10 Mbps不等;而交换式以太网正是广泛应用的以太网,可运行在100、1000和10000Mbps那样的高速率,分别以快速以太网、千兆以太网和万兆以太网的形式呈现。
二,OSI七层模型和TCP/IP五层模型
"OSI模型 ,即开放式通信系统互联参考模型 (Open System Interconnection Reference Model),是国际标准化组织(ISO)提出的一个试图使各种计算机在世界范围内互连为网络的标准框架,简称OSI。"
OSI七层模型
OSI定义了网络互连的七层模型(物理层、数据链路层、网络层、传输层、会话层、表示层、应用层),如下图所示:
**应用层:为应用程序或用户请求提供各种请求服务。**OSI参考模型最高层,也是最靠近用户的一层,为计算机用户、各种应用程序以及网络提供接口,也为用户直接提供各种网络服务。
**表示层:数据编码、格式转换、数据加密。**提供各种用于应用层数据的编码和转换功能,确保一个系统的应用层发送的数据能被另一个系统的应用层识别。如果必要,该层可提供一种标准表示形式,用于将计算机内部的多种数据格式转换成通信中采用的标准表示形式。数据压缩和加密也是表示层可提供的转换功能之一。
**会话层:创建、管理和维护会话。**接收来自传输层的数据,负责建立、管理和终止表示层实体之间的通信会话,支持它们之间的数据交换。该层的通信由不同设备中的应用程序之间的服务请求和响应组成。
**传输层:数据通信。**建立主机端到端的链接,为会话层和网络层提供端到端可靠的和透明的数据传输服务,确保数据能完整的传输到网络层。
**网络层:IP选址及路由选择。**通过路由选择算法,为报文或通信子网选择最适当的路径。控制数据链路层与传输层之间的信息转发,建立、维持和终止网络的连接。数据链路层的数据在这一层被转换为数据包,然后通过路径选择、分段组合、顺序、进/出路由等控制,将信息从一个网络设备传送到另一个网络设备。
**数据链路层:提供介质访问和链路管理。**接收来自物理层的位流形式的数据,封装成帧,传送到网络层;将网络层的数据帧,拆装为位流形式的数据转发到物理层;负责建立和管理节点间的链路,通过各种控制协议,将有差错的物理信道变为无差错的、能可靠传输数据帧的数据链路。
**物理层:管理通信设备和网络媒体之间的互联互通。**传输介质为数据链路层提供物理连接,实现比特流的透明传输。实现相邻计算机节点之间比特流的透明传送,屏蔽具体传输介质和物理设备的差异。
TCP/IP五层模型
TCP/IP是一组协议的代名词,它包括许多协议,组成了TCP/IP协议簇。它是把OSI七层模型简化成了五层模型。每一层都呼叫它的下一层所提供的网络来完成自己的需求
TCP/IP 五层协议和 OSI 的七层协议对应关系如下:
从上图中可以看出,TCP/IP
模型⽐OSI
模型更加简洁,它把 应⽤层/表示层/会话层
全部整合为了 应⽤层
。
在每⼀层都⼯作着不同的设备,⽐如我们常⽤的交换机就⼯作在数据链路层的,⼀般的路由器是⼯作在⽹络层的。
在每⼀层实现的协议也各不同,即每⼀层的服务也不同,下图列出了每层主要的传输协议:
一般说的FPGA UDP通信,FPGA只做到了传输层 ,传输层以上的会话层、表示层等等,FPGA是没有的。FPGA 开发板通过一片 以太网PHY芯片 提供对以太网连接的支持,PHY芯片 内提供物理层 ,进行4b/10b编码,PHY芯片提供MII/GMII/RGMII 接口的MAC连接。
在传输层中 TCP
和 UDP
都是传输层协议,它们都属于TCP/IP
协议族:
🤔 UDP
UDP的全称是**⽤户数据报** 协议,在⽹络中它与TCP协议⼀样⽤于处理数据包,是⼀种**⽆连接** 的协议。在OSI模型中,在传输层,处于IP协议的上⼀层。UDP有 不提供数据包分组、组装和不能对数据包进⾏排序的缺点
,也就是说,当报⽂发送之后,是⽆法得知其是否安全完整到达的。
它的特点如下:
- 面向无连接
- 有单播、多播、广播的功能
- 面向报文
- 不可靠性
- 头部开销⼩,传输数据报⽂⾼效。
🧐 TCP
- 面向连接
- 仅支持单播传输
- 面向字节流
- 可靠传输
- 提供拥塞控制
- 提供全双工通信
😜 TCP和UDP的区别
三,FPGA UDP通信硬件构成
根据以上的简述,我们知道 FPGA UDP通信 FPGA只做到了传输层 ,传输层以上的会话层、表示层等等,FPGA是没有的。 所以PC端 发送数据经过传输层添加TCP/UDP 头部 后,在经过网络层添加IP头部 ,然后经过数据链路层添加MAC头部,通过层级组包传输到FPGA的PHY芯片 内提供物理层 ,进行4b/10b编码,PHY芯片提供MII/GMII/RGMII 接口的MAC连接 。
硬件简化图如下所示:
组包简化图如下所示:
四,PHY芯片接口介绍
从数据传输角度来看,控制器(FPGA ) 和 PHY 侧芯片 实现以太网传输的数据链路两端,有 3 种主要的接口形式。这 3 种接口形式主要是MII GMII 和 RGMII 。 MII 主要应用在百兆网 传输中,而 GMII 和 RGMII 则均可以运用于千兆网, RGMII 相较于 GMII ,则可以有更高的数据位通信效率。
MII 接口
MII 接口信号连接关系及各信号的介绍如下。
GMII 接口
GMII 接口信号连接关系及各信号的介绍如下。
GMII 发送和接收时序:
RGMII 接口
RGMII 即ReducedGMII,是GMII 的简化版本,将接口信号线数量从24根减少到14根,时钟频率仍旧为125MHz,TX/RX 数据宽度从8 位变为4位。RGMII接口信号连接关系及各信号的介绍如下。
RGMII接口为了保持1000Mbps 的传输速率不变, RGMII 接口在时钟的上升沿和下降沿都采样数据。在参考时钟的上升沿发送 GMII 接口中的 TXD[3:0]/RXD[3:0] ,在参考时钟的下降沿发送 GMII 接口中的 TXD[7:4]/RXD[7:4] 。
RGMII 的时序分为两种:延时模式和非延时模式 ,可以通过配置PHY芯片改变模式。 用的比较多的模式是延时模式,一般PHY芯片默认配置为延时模式。
时序图如下:
根据以上介绍,我们使用RGMII接口的以太网 PHY 与 MAC (PHY )的连接实现方法,解决了接口问题,才能编写对应的网络协议实现逻辑。
五,RGMII和GMII转换电路设计
在以上了解中,我们知道RGMII 是GMII 的简化版本,接口信号线数量从24根减少到14根,TX/RX 数据宽度从8 位变为4位,所以我们要实现RGMII的发送与接收。
RGMII发送
对于FPGA来说,实现 RGMII 接口的发送是一个非常直接的过程,整个发送逻辑框图如图所示:
设计实现时,我们需要使用xilinx 的ODDR(Output Double Data Rate,输出双倍数据速率)原语,将该接口使用OLOGIC 块实现。ODDR 原语只有一个时钟输入,下降沿数据由输入时钟的本地反转来计时,反馈到I/O块的所有的时钟被完全复用,ODDR 原语的框图如图 所示:
其中各个端口的功能及描述如下:
除了这些端口外, ODDR原语还包含一些可用属性:
编写rgmii_send代码:
module rgmii_send(
input reset ,
output phy_rgmii_tx_clk ,
output phy_rgmii_tx_ctl ,
output [3:0] phy_rgmii_tx_data ,
input gmii_tx_clk ,
input gmii_tx_vld ,
input [7:0] gmii_tx_data
);
wire rgmii_tx_ctl;
wire [3:0] rgmii_tx_data;
/*------------------------------------------*\
ODDR
\*------------------------------------------*/
ODDR #(
.DDR_CLK_EDGE("SAME_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE"
.INIT(1'b0), // Initial value of Q: 1'b0 or 1'b1
.SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC"
) oddr_tx_rgmii_ctl (
.Q(rgmii_tx_ctl), // 1-bit DDR output
.C(gmii_tx_clk), // 1-bit clock input
.CE(1), // 1-bit clock enable input
.D1(gmii_tx_vld), // 1-bit data input (positive edge)
.D2(gmii_tx_vld), // 1-bit data input (negative edge)
.R(0), // 1-bit reset
.S(0) // 1-bit set
);
genvar i_tx;
generate
for ( i_tx = 0; i_tx < 4; i_tx = i_tx + 1) begin
ODDR #(
.DDR_CLK_EDGE("SAME_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE"
.INIT(1'b0), // Initial value of Q: 1'b0 or 1'b1
.SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC"
) oddr_tx_rgmii_ctl (
.Q(rgmii_tx_data[i_tx]), // 1-bit DDR output
.C(gmii_tx_clk), // 1-bit clock input
.CE(1), // 1-bit clock enable input
.D1(gmii_tx_data[i_tx]), // 1-bit data input (positive edge)
.D2(gmii_tx_data[i_tx + 4]), // 1-bit data input (negative edge)
.R(0), // 1-bit reset
.S(0) // 1-bit set
);
end
endgenerate
/*------------------------------------------*\
OBUF
\*------------------------------------------*/
OBUF obuf_tx_rgmii_clk(
.O(phy_rgmii_tx_clk), // Buffer output (connect directly to top-level port)
.I(gmii_tx_clk) // Buffer input
);
OBUF obuf_tx_rgmii_ctl(
.O(phy_rgmii_tx_ctl), // Buffer output (connect directly to top-level port)
.I(rgmii_tx_ctl) // Buffer input
);
genvar j_tx;
generate
for ( j_tx = 0; j_tx < 4; j_tx = j_tx + 1) begin
OBUF obuf_tx_rgmii_data(
.O(phy_rgmii_tx_data[j_tx]), // Buffer output (connect directly to top-level port)
.I(rgmii_tx_data[j_tx]) // Buffer input
);
end
endgenerate
endmodule
RGMII接收
对于 FPGA 来说,实现 RGMII 接口的接收同样是一个非常直接的过程, 整个接收逻辑框图如图所示:
同样,设计实现时,可通过使用xilinx 的IDDR原语,将该接口使用ILOGIC 块实现。在ILOGIC 块中,有着专用的寄存器,用于实现输入双倍数据速率(DDR)寄存器,当我们实例化IDDR 原语时便会自动访问该功能。IDDR 原语的框图如图所示:
其中各个端口的功能及描述如表
除了这些端口外, IDDR 原语还包含一些可用属性:
编写rgmii_receive代码:
`timescale 1ns / 1ps
module rgmii_receive(
input reset ,
input delay_refclk ,
input phy_rgmii_rx_clk ,
input phy_rgmii_rx_ctl ,
input [3:0] phy_rgmii_rx_data ,
output gmii_rx_clk ,
output gmii_rx_vld ,
output gmii_rx_error ,
output [7:0] gmii_rx_data
);
wire phy_rgmii_rx_clk_ibuf;
wire phy_rgmii_rx_ctl_ibuf;
wire [3:0] phy_rgmii_rx_data_ibuf;
// wire phy_rgmii_rx_ctl_delay;
// wire [3:0] phy_rgmii_rx_data_delay;
wire gmii_rx_error_xor ;
assign gmii_rx_error = gmii_rx_vld ^ gmii_rx_error_xor;
// assign gmii_rx_clk = phy_rgmii_rx_clk;
/*------------------------------------------*\
IBUF
\*------------------------------------------*/
IBUF rgmii_rx_clk_ibuf (
.O(phy_rgmii_rx_clk_ibuf), // Buffer output
.I(phy_rgmii_rx_clk) // Buffer input (connect directly to top-level port)
);
IBUF rgmii_rx_ctl_ibuf (
.O(phy_rgmii_rx_ctl_ibuf), // Buffer output
.I(phy_rgmii_rx_ctl) // Buffer input (connect directly to top-level port)
);
genvar i_rx;
generate
for ( i_rx = 0; i_rx < 4; i_rx = i_rx + 1) begin
IBUF rgmii_rx_data_ibuf (
.O(phy_rgmii_rx_data_ibuf[i_rx]), // Buffer output
.I(phy_rgmii_rx_data[i_rx]) // Buffer input (connect directly to top-level port)
);
end
endgenerate
/*------------------------------------------*\
BUFG 、BUFIO
\*------------------------------------------*/
BUFG rgmii_rx_clk_bufg (
.O(gmii_rx_clk), // 1-bit output: Clock output
.I(phy_rgmii_rx_clk_ibuf) // 1-bit input: Clock input
);
// BUFIO rgmii_rx_clk_bufio (
// .O(phy_rgmii_rx_clk_bufio), // 1-bit output: Clock output (connect to I/O clock loads).
// .I(phy_rgmii_rx_clk) // 1-bit input: Clock input (connect to an IBUF or BUFMR).
// );
/*-----------------------------------------*\
IDDR
\*-----------------------------------------*/
IDDR #(
.DDR_CLK_EDGE("SAME_EDGE_PIPELINED"), // "OPPOSITE_EDGE", "SAME_EDGE"
// or "SAME_EDGE_PIPELINED"
.INIT_Q1(1'b0), // Initial value of Q1: 1'b0 or 1'b1
.INIT_Q2(1'b0), // Initial value of Q2: 1'b0 or 1'b1
.SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC"
) iddr_rgmii_rx_ctl (
.Q1(gmii_rx_vld), // 1-bit output for positive edge of clock
.Q2(gmii_rx_error_xor), // 1-bit output for negative edge of clock
.C(phy_rgmii_rx_clk_ibuf), // 1-bit clock input
.CE(1), // 1-bit clock enable input
.D(phy_rgmii_rx_ctl_ibuf), // 1-bit DDR data input
.R(0), // 1-bit reset
.S(0) // 1-bit set
);
genvar q_rx;
generate
for (q_rx = 0; q_rx<4 ; q_rx = q_rx+ 1) begin
IDDR #(
.DDR_CLK_EDGE("SAME_EDGE_PIPELINED"), // "OPPOSITE_EDGE", "SAME_EDGE"
// or "SAME_EDGE_PIPELINED"
.INIT_Q1(1'b0), // Initial value of Q1: 1'b0 or 1'b1
.INIT_Q2(1'b0), // Initial value of Q2: 1'b0 or 1'b1
.SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC"
) iddr_rgmii_rx_data (
.Q1(gmii_rx_data[q_rx]), // 1-bit output for positive edge of clock
.Q2(gmii_rx_data[q_rx + 4]), // 1-bit output for negative edge of clock
.C(phy_rgmii_rx_clk_ibuf), // 1-bit clock input
.CE(1), // 1-bit clock enable input
.D(phy_rgmii_rx_data_ibuf[q_rx]), // 1-bit DDR data input
.R(0), // 1-bit reset
.S(0) // 1-bit set
);
end
endgenerate
endmodule
RGMII 顶层
RGMII 接口分别实现了接收和发送两部分,将两部分例化封装顶层rgmii_interface:
module rgmii_interface(
input reset ,
input delay_refclk ,
input phy_rgmii_rx_clk ,
input phy_rgmii_rx_ctl ,
input [3:0] phy_rgmii_rx_data ,
output gmii_rx_clk ,
output gmii_rx_vld ,
output gmii_rx_error ,
output [7:0] gmii_rx_data ,
output phy_rgmii_tx_clk ,
output phy_rgmii_tx_ctl ,
output [3:0] phy_rgmii_tx_data ,
input gmii_tx_clk ,
input gmii_tx_vld ,
input [7:0] gmii_tx_data
);
rgmii_receive rgmii_receive
(
.reset (reset),
.delay_refclk (delay_refclk),
.phy_rgmii_rx_clk (phy_rgmii_rx_clk),
.phy_rgmii_rx_ctl (phy_rgmii_rx_ctl),
.phy_rgmii_rx_data (phy_rgmii_rx_data),
.gmii_rx_clk (gmii_rx_clk),
.gmii_rx_vld (gmii_rx_vld),
.gmii_rx_error (gmii_rx_error),
.gmii_rx_data (gmii_rx_data)
);
rgmii_send rgmii_send
(
.reset (reset),
.phy_rgmii_tx_clk (phy_rgmii_tx_clk),
.phy_rgmii_tx_ctl (phy_rgmii_tx_ctl),
.phy_rgmii_tx_data (phy_rgmii_tx_data),
.gmii_tx_clk (gmii_tx_clk),
.gmii_tx_vld (gmii_tx_vld),
.gmii_tx_data (gmii_tx_data)
);
endmodule
六,总结
至此,关于FPGA UDP通信的RGMII 接口与 GMII 接口 的互转逻辑设计已经实现,在 FPGA 中设计以太网的接收和发送逻辑时,只需要按照 GMII 接口的形式,先设计出对应的发送和接收逻辑,再将对应的端口连接到 RGMII 与 GMII 接口转换逻辑上,就能够完成基于 RGMII 接口的以太网接收和发送。
接下来,在下一篇博客中将会实现数据链路层**(mac层)**的接收与发送。