FPGA+AI工程师技术社区
标题: [MLKPAI-F01-EG4D]FPGA_以太网通信连载-02UDP三速以太网设计 [打印本页]
作者: UT发布 时间: 2025-4-3 14:50
标题: [MLKPAI-F01-EG4D]FPGA_以太网通信连载-02UDP三速以太网设计
本帖最后由 UT发布 于 2025-4-19 10:04 编辑
软件版本:TD_5.6.4_Release_97693
操作系统:WIN10 64bit
硬件平台:适用安路(Anlogic)FPGA
1 以太网PHY芯片介绍使用以太网协议进行数据传输时,经常需要将数据进行编解码和数模转换才能让其在物理介质(网线)上传输,这些功能被定义在在OSI模型中的物理层中,通常FPGA芯片不具备这样的功能,这就需要使用外部的PHY芯片来完成这些功能。常见的千兆以太网PHY芯片有YT8531,RTL8211,88E1518等,YT8531芯片的结构功能框图如下图所示。
以太网MAC层通过RGMII接口和PHY芯片的PCS层连接,PCS(Physical Coding Sublayer,物理编码子层)主要完成数据的编码/解码、加扰/解扰。PMA(Physical Medium Attachment,物理媒介子层)在发送端通过DAC模块完成数据的数模转换,在接收端具有时钟恢复、均衡器、ADC等功能模块。在PHY芯片上有一组内部寄存器的配置接口MDIO(Management Data Input/Output Interface)和MDC,通过一定的时序规范可以读取PHY芯片内部寄存器的配置信息,或者将配置信息写入PHY芯片中。LED控制器可以通过控制LED的亮灭指示当前以太网协商的速率和工作状态。
PHY芯片的一些功能需要通过引脚的硬件连接来配置,YT8531的引脚功能如下图所示。
(, 下载次数: 40)
PHYAD引脚控制PHY芯片的物理地址,与IIC总线类似,一组MDIO总线可以连接多个设备,每个设备都需要设置独立的地址,主机利用这个地址对不同的设备进行访问。通过4.7千欧的电阻对TXDLY和RXDLY进行上拉或下拉,可以控制PHY芯片是否对发送时钟和接收时钟进行延时,对1000Mbps速率,上拉会给时钟端2ns延时,以让时钟的边沿处于数据的中心,更容易采集到稳定的数据。CFG_EXT引脚控制是否开启芯片内部的LDO电源供电,CFG_LDO0和CFG_LDO1引脚控制IO电压标准。
一般PHY芯片仅通过硬件连接配置即可正常使用,若还需要修改其它功能如速率、半双工等,需要使用软件配置的方法,即通过MDIO接口配置PHY寄存器。PHY寄存器的地址空间为5位,802.3协议中规定了地址为0-15的寄存器功能,其余寄存器可由厂商自定义。寄存器0为PHY控制寄存器,该寄存器的功能如下图所示。
(, 下载次数: 35)
Reset:软件复位,将位15设置为1来复位PHY芯片,复位过程中该位保持为1,复位完成后自动清零,复位完成前PHY芯片不接受写入寄存器的操作。
Loopback:位14默认为0,当设置为1时PHY芯片为回环模式,从MAC发送过来的数据会通过PCS端直接回环到MAC接收端,通常应用于故障诊断中。
Autoneg_En:位12为自动协商使能,设置为1时端口的工作模式通过和对端进行自动协商来确定,设置为0时通过其它位的相应配置来确定。
Speed_Selection:位13和位6联合控制端口的速率,前提是自动协商关闭,该设置才起作用。位13为低位,位6为高位,Speed_Selection设置为2’b00时,端口速率为10Mbps,设置为2’b01时,端口速率为100Mbps,设置为2’b10时,端口速率为1000Mbps,2’b11保留。
Duplex_Mode:位8控制端口是全双工模式还是半双工模式,需要自动协商关闭该设置才起作用。
Power_Down:位11设置为1时,PHY芯片掉电,当从掉电中恢复时,会进行软件复位和自动协商。
通过配置寄存器0就能将PHY配置成我们想要的工作状态,其它寄存器的功能可以在手册中查看。
2 MDIO接口设计
2.1 MDIO时序介绍MDIO接口在协议802.3中定义,主要用于以太网的MAC和PHY之间,MAC层器件通过MDIO配置PHY芯片的工作模式或者获取当前的工作状态。MDIO又称为SMI(Serial Management Interface,串行管理接口),由MDIO数据线和MDC时钟线两条信号线组成,是串行半双工的数据接口,其接口通信协议规范如下图所示。
(, 下载次数: 36)
Preamble:32位前导码,连续发送32位高电平用于PHY芯片同步。
ST:2’b01作为数据开始的标志。
OP:2位操作码,2’b10时表示读操作,2’b01时表示写操作。
PHYAD:PHY硬件地址,高2位默认为2’b00,低3位由PHY芯片引脚的上下拉控制,通过该字段决定和哪个PHY芯片通信。
REGAD:寄存器地址,用于设置该次操作的寄存器对象。
TA:空闲时间,防止总线上产生竞争,在写操作时,FPGA驱动信号线传输2’b10。读操作时,FPGA释放总线,将MDIO驱动成高阻态,在传输第一个比特时,PHY芯片也驱动信号线为高阻态,传输第二个比特时,PHY芯片接过总线控制权,将信号线驱动为低电平。
DATA:16位数据,写操作时由FPGA驱动,读操作时由PHY芯片驱动。
以PHY芯片硬件地址为0x01为例,MDIO接口的读写时序如下图所示。
MDC时钟由FPGA侧驱动,时钟下降沿更新数据,上升沿采样数据。当总线处于空闲状态或一次读写操作完成时,FPGA和PHY芯片都必须释放总线,即将MDIO信号线驱动为高阻态。
2.2 MDIO程序设计
MDIO驱动器主要包含MDIO收发数据状态机、MDC时钟分频器、移位发送模块、移位接收模块、总线忙控制模块。MDIO和MDC的输出逻辑和时序通过状态机控制。
接口信号功能如下:
IO_phy_mdio为MDIO双向数据线。
O_phy_mdc为MDC时钟线。
I_reg_addr为操作的寄存器地址。
I_phy_addr为PHY的物理地址。
I_wr_data为写入寄存器的数据。
O_rd_data为从寄存器读出的数据。
I_mdio_mode为总线的操作模式,置1时为写操作,置0时为读操作。
I_mdio_req为握手请求信号。
O_mdio_busy为握手响应信号,当空闲状态接收到请求时,该信号拉高,直到一次操作完成后,该信号拉低,等待下一次握手请求信号。
IDLE:空闲状态,状态机处于空闲状态时,将总线置为高阻态,当接收到REQ请求时,开始一次操作,进入PREAMBLE状态。
PREAMBLE:发送前导码,发送32bit的逻辑1,用于同步,发送完成后进入INFO状态
INFO:发送开始标志、操作码、物理地址、寄存器地址共14bit数据,发送完成后进入TA状态。
TA:2个时钟周期用来转换总线控制权。
RD_DATA:将总线上的串行数据转成16bit并行数据。
WR_DATA:将需要写入寄存器的数据由并行转成串行输出。
PAUSE:一次操作完成后暂停一段时间间隔,才能允许下一次操作。
- module uimdio_ctrl#(
- parameter DIV_NUM = 25
- )
- ( input wire I_clk ,//主时钟
- input wire I_rstn ,//复位,低有效
- input wire [4:0] I_phy_addr ,//硬件地址
- input wire [4:0] I_reg_addr ,//寄存器地址
- input wire I_mdio_req ,//操作请求
- input wire I_mdio_mode ,//操作模式,0write,1read
- input wire [15:0] I_wr_data ,//写寄存器数据
- output reg [15:0] O_rd_data ,//读寄存器数据
- output wire O_mdio_busy ,//操作忙标志
- output reg O_mdio_done ,//读完成标志
- output wire O_phy_mdc ,//MDIO时钟线
- inout wire IO_phy_mdio //MDIO数据线
- );
- localparam IDLE = 0;//空闲
- localparam PREAMBLE = 1;//前导码
- localparam INFO = 2;//START + OP + PHYADDR + REGADDR
- localparam TA = 3;//控制权转换
- localparam WR_DATA = 4;//写寄存器
- localparam RD_DATA = 5;//读寄存器
- localparam PAUSE = 6;//两次操作间隔
- reg [2:0] cur_st ;
- reg [2:0] nxt_st ;
- reg [5:0] st_cnt ;
- reg div_clk ;
- reg [15:0] div_cnt ;
- reg mdio_out ;
- reg mdio_en ;
- assign O_mdio_busy = ~(cur_st == IDLE);
- assign O_phy_mdc = ~div_clk;//时钟反转,数据在时钟的下降沿跳变
- assign IO_phy_mdio = mdio_en ? mdio_out : 1'bz;
- //时钟分频计数器
- always@(posedge I_clk or negedge I_rstn) begin
- if(!I_rstn) begin
- div_cnt <= 0;
- div_clk <= 0;
- end
- else if(div_cnt < DIV_NUM - 1) begin
- div_cnt <= div_cnt + 1;
- div_clk <= div_clk;
- end
- else begin
- div_cnt <= 0;
- div_clk <= ~div_clk;
- end
- end
- //状态机计数器
- always@(posedge div_clk or negedge I_rstn) begin
- if(!I_rstn)
- st_cnt <= 0;
- else if(nxt_st != cur_st)
- st_cnt <= 0;
- else
- st_cnt <= st_cnt + 1;
- end
- //三段式状态机
- always@(posedge div_clk or negedge I_rstn) begin
- if(!I_rstn)
- cur_st <= IDLE;
- else
- cur_st <= nxt_st;
- end
- always@(*) begin
- if(!I_rstn)
- nxt_st <= IDLE;
- else begin
- case(cur_st)
- IDLE:begin
- if(I_mdio_req)
- nxt_st <= PREAMBLE;
- else
- nxt_st <= IDLE;
- end
- PREAMBLE:begin
- if(st_cnt == 'd31)
- nxt_st <= INFO;
- else
- nxt_st <= PREAMBLE;
- end
- INFO:begin
- if(st_cnt == 'd13)
- nxt_st <= TA;
- else
- nxt_st <= INFO;
- end
- TA:begin
- if(st_cnt == 'd1)
- if(I_mdio_mode)
- nxt_st <= WR_DATA;
- else
- nxt_st <= RD_DATA;
- else
- nxt_st <= TA;
- end
- WR_DATA:begin
- if(st_cnt == 'd15)
- nxt_st <= PAUSE;
- else
- nxt_st <= WR_DATA;
- end
- RD_DATA:begin
- if(st_cnt == 'd16)
- nxt_st <= PAUSE;
- else
- nxt_st <= RD_DATA;
- end
- PAUSE:begin
- if(st_cnt == 'd31)
- nxt_st <= IDLE;
- else
- nxt_st <= PAUSE;
- end
- default:begin
- nxt_st <= IDLE;
- end
- endcase
- end
- end
- always@(posedge div_clk or negedge I_rstn) begin
- if(!I_rstn)
- mdio_out <= 0;
- else if(cur_st == PREAMBLE)
- mdio_out <= 1;
- else if(cur_st == INFO) begin
- case(st_cnt)
- 'd0 :mdio_out <= 0;
- 'd1 :mdio_out <= 1;
- 'd2 :mdio_out <= I_mdio_mode ? 0 : 1;
- 'd3 :mdio_out <= I_mdio_mode ? 1 : 0;
- 'd4 :mdio_out <= I_phy_addr[4];
- 'd5 :mdio_out <= I_phy_addr[3];
- 'd6 :mdio_out <= I_phy_addr[2];
- 'd7 :mdio_out <= I_phy_addr[1];
- 'd8 :mdio_out <= I_phy_addr[0];
- 'd9 :mdio_out <= I_reg_addr[4];
- 'd10 :mdio_out <= I_reg_addr[3];
- 'd11 :mdio_out <= I_reg_addr[2];
- 'd12 :mdio_out <= I_reg_addr[1];
- 'd13 :mdio_out <= I_reg_addr[0];
- endcase
- end
- else if(cur_st == TA) begin
- case(st_cnt)
- 'd0 :mdio_out <= I_mdio_mode ? 1 : 1'bz;
- 'd1 :mdio_out <= I_mdio_mode ? 0 : 1'bz;
- endcase
- end
- else if(cur_st == WR_DATA)
- mdio_out <= I_mdio_mode ? I_wr_data[15 - st_cnt] : 0;
- else
- mdio_out <= 0;
- end
- always@(posedge div_clk or negedge I_rstn) begin
- if(!I_rstn)
- mdio_en <= 0;
- else if(cur_st == IDLE || cur_st == PAUSE)
- mdio_en <= 0;
- else if(cur_st == TA)
- mdio_en <= I_mdio_mode ? 1 : 0;
- else if(cur_st == PREAMBLE)
- mdio_en <= 1;
- else
- mdio_en <= mdio_en;
- end
- always@(posedge div_clk or negedge I_rstn) begin
- if(!I_rstn)
- O_mdio_done <= 0;
- else if(cur_st == RD_DATA && st_cnt == 'd16)
- O_mdio_done <= 1;
- else
- O_mdio_done <= 0;
- end
- //MDIO读数据,使用mdc时钟采样
- always@(posedge O_phy_mdc or negedge I_rstn) begin
- if(!I_rstn)
- O_rd_data <= 0;
- else if(cur_st == RD_DATA && st_cnt >= 1)
- O_rd_data <= {O_rd_data[14:0], IO_phy_mdio};
- else
- O_rd_data <= O_rd_data;
- end
- endmodule
复制代码为了方便操作MDIO驱动器的读写,需要增加一级模块封装,该模块完成对请求信号及操作类型的控制,来对PHY寄存器进行配置。在PHY寄存器配置完成后,通过按键可以进行读操作,将寄存器的配置信息读取出来。
(, 下载次数: 30)
在上电后等待一定的时间间隔,释放复位,通过握手信号控制驱动器的工作状态,当busy信号为0时,拉高req信号,直到busy信号为高,代表驱动器响应成功,直到busy再次拉低,进行下一次操作。通过计数器控制写操作执行的次数,当达到指定次数时,寄存器配置完成,拉高cfgdone信号,表示不再继续进行写操作。
第一次写操作在bit11的位置写入1,让PHY芯片掉电,第二次写操作再写入速度模式和全双工模式。
- module uiphy_cfg#(
- parameter SPEED = 2'b01,
- parameter CLK_FREQ = 32'd25_000_000
- )
- (
- input wire I_clk ,
- input wire I_rstn ,
- input wire I_keycap ,
- output wire O_phyrst ,
- output wire O_phy_mdc ,
- inout wire IO_phy_mdio
- );
- localparam CFG_NUM = 3;
- localparam IDLE = 0;
- localparam REQ = 1;
- localparam DONE = 2;
- reg [23:0] cnt;
- reg [5:0] index;
- reg [23:0] gap_cnt;
- reg [1:0] state;
- reg cfgdone;
- reg mdio_req;
- reg mode;
- wire [4:0] reg_addr;
- wire [15:0] rd_data;
- reg [15:0] wr_data;
- wire mdio_busy;
- wire mdio_done;
- wire start_lock;
- assign start_lock = cnt == 1250_000;
- assign reg_addr = cfgdone ? 5'h0 : 5'h0;
- always@(posedge I_clk or negedge O_phyrst) begin
- if(!O_phyrst)
- cnt <= 0;
- else if(cnt < 1250_000)
- cnt <= cnt + 1'b1;
- else
- cnt <= cnt;
- end
- always@(posedge I_clk or negedge O_phyrst) begin
- if(!O_phyrst)
- state <= IDLE;
- else if(start_lock) begin
- case(state)
- IDLE:begin
- if(cfgdone == IDLE)
- state <= REQ;
- else if(cfgdone && I_keycap)
- state <= REQ;
- else
- state <= IDLE;
- end
- REQ:begin
- if(mdio_busy)
- state <= DONE;
- else
- state <= REQ;
- end
- DONE:begin
- if(!mdio_busy)
- state <= IDLE;
- else
- state <= DONE;
- end
- default:begin
- state <= IDLE;
- end
- endcase
- end
- end
- always@(posedge I_clk or negedge O_phyrst) begin
- if(!O_phyrst)
- mdio_req <= 0;
- else if(mdio_req && mdio_busy)
- mdio_req <= 0;
- else if(state == REQ)
- mdio_req <= 1;
- else
- mdio_req <= 0;
- end
- always@(posedge I_clk or negedge O_phyrst) begin
- if(!O_phyrst)
- mode <= 0;
- else if(state == REQ && cfgdone == 1)
- mode <= 0;
- else if(state == REQ)
- mode <= 1;
- else
- mode <= mode;
- end
- always@(posedge I_clk or negedge O_phyrst) begin
- if(!O_phyrst)
- cfgdone <= 0;
- else if(index == CFG_NUM - 1 && !mdio_busy)
- cfgdone <= 1;
- end
- always@(posedge I_clk or negedge O_phyrst) begin
- if(!O_phyrst)
- index <= 0;
- else if(state == DONE && !mdio_busy && !cfgdone)
- index <= index + 1;
- else
- index <= index;
- end
- always@(*) begin
- case(index)
- 0:wr_data <= 16'h0800;
- 1:wr_data <= {2'b00, SPEED[0], 6'b0000_10, SPEED[1], 6'b0000_00};
- 2:wr_data <= {2'b10, SPEED[0], 6'b0000_10, SPEED[1], 6'b0000_00};
- default:wr_data <= 16'b8000;
- endcase
- end
- uiphyrst#(
- .CLK_FREQ (CLK_FREQ)
- )
- uiphyrst
- (
- .I_CLK (I_clk ),
- .I_rstn (I_rstn ),
- .I_phyrst (I_rstn ),
- .O_phyrst (O_phyrst )
- );
- uimdio_ctrl #(
- .DIV_NUM (50 )
- )
- uimdio_ctrl
- (
- .I_clk (I_clk ),
- .I_rstn (O_phyrst ),
- .I_phy_addr (5'b00100 ),
- .I_reg_addr (reg_addr ),
- .I_mdio_req (mdio_req ),
- .I_mdio_mode (mode ),
- .I_wr_data (wr_data ),
- .O_rd_data (rd_data ),
- .O_mdio_busy (mdio_busy ),
- .O_mdio_done (mdio_done ),
- .O_phy_mdc (O_phy_mdc ),
- .IO_phy_mdio (IO_phy_mdio )
- );
- endmodule
复制代码- `timescale 1ns/1ps
- module cfg_tb;
- reg clk,rst;
- wire phyrst,phy_mdc,phy_mdio;
- pullup(phy_mdio);
- uiphy_cfg #(
- .SPEED(2'b01),
- .CLK_FREQ(32'd25_000_000)
- ) inst_uiphy_cfg (
- .I_clk (clk),
- .I_rstn (~rst),
- .O_phyrst (phyrst),
- .O_phy_mdc (phy_mdc),
- .IO_phy_mdio (phy_mdio)
- );
- always #20 clk = ~clk;
- initial begin
- clk = 0;
- rst = 1;
- #200;
- rst = 0;
- end
- endmodule
复制代码通过modelsim进行仿真验证,抓取mdio_req为高电平的时间段,可见mdio_req拉高后,MDIO驱动器将mdio_busy也拉高进行响应,当mdio_busy拉高时,mido_req立即拉低,握手成功。
mdio信号线上的数据如下图所示,空闲时总线为高阻态,在mdio_busy拉高后,进行写操作,总线先发送32bit的逻辑1,接着按顺序发送开始标志2’b01、操作码2’b01、物理地址5’b00100、寄存器地址5’b00000、TA2’b10共16bit数据,然后发送数据16’h0800,全部发送完成后,总线置为高阻态。
(, 下载次数: 42)
通过MDIO将PHY芯片的工作模式设置为全双工、100Mbps速率,在PC中的“网络和Internet”设置中查看以太网的配置信息,可以看到链接速度为100Mbps,证明PHY芯片配置成功。
(, 下载次数: 35)
然后在程序中设置按键控制读寄存器0操作,通过chipwatcher抓取波形,上板验证效果如下图所示。读出寄存器0的配置信息为16’h2100,即为写入的配置信息。
(, 下载次数: 34)
3 RGMII接口设计
3.1 RGMII接口时序
在2.2节我们对RGMII接口的功能及引脚做了介绍,RGMII接口的时序如下图所示。
(, 下载次数: 38)
在普通模式下,数据在时钟的边沿变化,如果不做额外处理,数据无法被稳定采样,所以PHY芯片还提供了延迟模式来保证数据在稳定的区间内被采样。发送端或接受端被设置成延时模式时,时钟在PHY芯片内部会添加2ns的延时,让时钟的边沿对齐数据有效区间的中心。延时模式通过PHY芯片的引脚上下拉控制。
RGMII接口是双沿采样的,每个时钟周期传输一个字节的数据。传输数据时,在时钟上升沿传输一个字节的低4bit,在时钟下降沿传输一个字节的高4bit,所以千兆速率下RGMII接口的采样时钟为125MHz,而接口速率为1000Mbps。FPGA内部逻辑难以处理双沿数据,需要在接收引脚或发送引脚的IOB上将双沿数据转换成单沿数据或将单沿数据转换成双沿数据,IOB中的IDDR和ODDR可以用来实现这些功能。
PH1_LOGIC_IDDR是安路PH1系列FPGA IO上将双沿采样时钟数据变换为单沿数据的单元。具有数据对齐的双速率D触发器,并具有同步/异步复位功能。可以将输入的DDR数据转为1位SDR数据同步到内部时钟网络。
(, 下载次数: 38)
该单元的引脚功能如下:
clk:采样时钟。
d:IO上输入的DDR数据。
rst:复位信号,高有效。
q0:1位上升沿数据输出。
q1:1位下降沿数据输出。
该单元可以配置成PIPED模式或NONE模式,PIPED模式下时序如下图所示。
(, 下载次数: 39)
NONE模式下时序图如下图所示。
(, 下载次数: 35)
工作在PIPED模式下时,q0和q1对应一个完整字节的数据,所以在对RGMII接口采样时使用PIPED模式。
PH1_LOGIC_ODDR时FPGA IO上将内部单沿数据变换为双沿数据后输出的单元,具有同步/异步复位功能。可以将数据双倍率输出到IO口上。
(, 下载次数: 36)
该单元的引脚功能如下:
clk:同步时钟。
d0:1位上升沿输入数据。
d1:1位下降沿输入数据。
rst:复位信号,高有效。
q:1位DDR双沿输出数据。
该单元的工作时序如下图所示。
(, 下载次数: 31)
3.2 RGMII模块设计RGMII接口输入输出模块rgmii_interface的代码如下:
- module rgmii_interface (
- // 指示当前运行速度为10/100
- input speed_10_100, //设置当前的运行速率
- //RGMII发送时序调整,当PHY 接收来自FPGA的TX信号,没有使用内部2ns延迟情况需要在fpga端设置2ns延迟
- input gmii_tx_clk_d,
- input gmii_tx_reset_d,
- // 以下端口是RGMII物理接口:这些端口将位于FPGA的引脚上
- output [3:0] rgmii_txd,
- output rgmii_tx_ctl,
- output rgmii_txc,
- input [3:0] rgmii_rxd,
- input rgmii_rx_ctl,
- input rgmii_rxc,
- // 以下端口连接到 TEMAC核 的 内部GMII接口模块
- input gmii_tx_reset,
- input gmii_tx_clk, // ggmii_tx_clk: 125mhz
- input [7:0] gmii_txd,
- input gmii_tx_en,
- input gmii_tx_er,
- input gmii_rx_reset,
- output gmii_rx_clk, //output: 125mhz(1gbps) 25mhz(100mbps) 2.5mhz(10mbps)
- output reg [7:0] gmii_rxd,
- output reg gmii_rx_dv,
- output reg gmii_rx_er,
- output gmii_crs,
- output gmii_col,
- // 以下信号为RGMII状态信号
- output reg link_status,
- output reg [1:0] clock_speed,
- output reg duplex_status
- );
- //----------------------------------------------------------------------------
- // 模块 内部 信号
- //----------------------------------------------------------------------------
- reg [3:0] gmii_txd_falling; // gmii_txd信号在gmii_tx_clk的下降沿锁存。
- wire rgmii_txc_odelay; // RGMII接收器时钟ODDR输出.
- wire rgmii_tx_ctl_odelay; // RGMII控制信号ODDR输出.
- wire [3:0] rgmii_txd_odelay; // RGMII数据ODDR输出.
- wire rgmii_tx_ctl_int; // 内部RGMII传输控制信号.
- wire rgmii_rx_ctl_delay;
- wire [3:0] rgmii_rxd_delay;
- wire rgmii_rx_ctl_reg; // 内部RGMII接收器控制信号.
- reg tx_en_to_ddr;
- wire gmii_rx_dv_reg;
- wire gmii_rx_er_reg;
- wire [7:0] gmii_rxd_reg;
- wire inband_ce; //RGMII带内状态寄存器 使能输出信号
- wire rgmii_rxc_int;
- //==============================================================================
- // RGMII 发送逻辑
- //==============================================================================
- //----------------------------------------------------------------------------
- // RGMII 发送器时钟管理:rgmii_txc
- //----------------------------------------------------------------------------
- // 产生 rgmii_txc 时钟.
- PH1_LOGIC_ODDR rgmii_txc_ddr
- (
- .q (rgmii_txc_odelay), //output 125mhz(1gbps) 25mhz(100mbps) 2.5mhz(10mbps)
- .clk (gmii_tx_clk_d),
- .d0 (1'b1),
- .d1 (1'b0),
- .rst (gmii_tx_reset_d)
- );
- assign rgmii_txc = rgmii_txc_odelay;
-
- //---------------------------------------------------------------------------
- // RGMII 发送逻辑 : rgmii_txd
- //---------------------------------------------------------------------------
- // 1Gbps gmii_txd 8位有效; rgmii_txc双沿使能发送8位
- // 10/100Mbps gmii_txd 仅低四位有效,rgmii_txc双沿使能重复发送低四位
- // 1Gbps时,125mhz的rgmii_txc,上升沿发送低四位,下降沿发送高四位。 一个时钟周期8bit, 125*8=1Gbps
- // 100Mbps时,25mhz的rgmii_txc,上升沿发送低四位,下降沿发送低四位。 相当于一个时钟周期只发一个4bit, 25*4=100mbps
- // 10Mbps时同100mbps,数据有效位在每byte的低四位,虽然一个时钟周期重复发低四位,相当于一个时钟周期只发一个低4位。
-
- always @ (speed_10_100, gmii_txd)begin
- if (speed_10_100 == 1'b0) // 1Gbps gmii_txd 8位有效
- gmii_txd_falling <= gmii_txd[7:4];
- else // 10/100Mbps gmii_txd高四位无效,rgmii_txc双沿使能发送低四位
- gmii_txd_falling <= gmii_txd[3:0];
- end
- genvar i;
- generate for (i=0; i<4; i=i+1)begin : txdata_out_bus
- PH1_LOGIC_ODDR rgmii_txd_out
- (
- .q (rgmii_txd_odelay[i]),
- .clk (gmii_tx_clk),
- .d0 (gmii_txd[i]),
- .d1 (gmii_txd_falling[i]),
- .rst (gmii_tx_reset)
- );
- assign rgmii_txd[i] = rgmii_txd_odelay[i];
- end
- endgenerate
- //---------------------------------------------------------------------------
- // RGMII 发送逻辑 : rgmii_tx_ctl
- //---------------------------------------------------------------------------
- // 编码 rgmii ctl 信号
- assign rgmii_tx_ctl_int = gmii_tx_en ^ gmii_tx_er;
- // 需要逻辑以确保 错误信号 将在整个时钟相位内,将tx_ctl发送为低电平
- always @(speed_10_100 or gmii_tx_en or gmii_tx_er)begin
- if (speed_10_100)
- tx_en_to_ddr = gmii_tx_en & (!gmii_tx_er );
- else
- tx_en_to_ddr = gmii_tx_en;
- end
-
- // oDDR primitive
- PH1_LOGIC_ODDR ctl_output
- (
- .q (rgmii_tx_ctl_odelay),
- .clk (gmii_tx_clk),
- .d0 (tx_en_to_ddr),
- .d1 (rgmii_tx_ctl_int),
- .rst (gmii_tx_reset)
- );
- assign rgmii_tx_ctl = rgmii_tx_ctl_odelay;
- //==============================================================================
- // RGMII 接收逻辑
- //==============================================================================
- //---------------------------------------------------------------------------
- // RGMII 接收逻辑:rgmii_rxc
- //---------------------------------------------------------------------------
- PH1_PHY_IOCLK u_rgmii_rxc_int
- (
- .clkin (rgmii_rxc),
- .clkout (rgmii_rxc_int)
- );
- PH1_PHY_SCLK_V2 u_rgmii_rxc
- (
- .ce(1'b1),
- .clkin (rgmii_rxc),
- .clkout (gmii_rx_clk)
- );
-
- //---------------------------------------------------------------------------
- // RGMII 接收逻辑:rgmii_rxd ----> gmii_rxd_reg
- //---------------------------------------------------------------------------
- // 1Gbps gmii_rxd 8位有效; rgmii_rxc双沿使能接收8位
- // 10/100Mbps gmii_rxd 仅低四位有效,rgmii_rxc双沿使能接收高低四位数据相同
- // 1gbps时,125mhz的rgmii_rxc,上升沿接收四位数据(对应低4bit),下降沿接收四位数据(对应高4bit)。 一个时钟周期8bit, 125mhz*8=1gbps
- // 100mbps时,25mhz的rgmii_rxc,上升沿接收四位数据,下降沿接收四位数据(高四位和低四位数据相同)。 相当于单沿采样,一个时钟周期只收一个4bit(数据有效位在每byte的低四位:高低四位重复), 25mhz*4=100mbps
- // 10mbps时同100mbps,虽然一个时钟周期上下沿重复接收相同四位数据,相当于一个时钟周期只收一个4bit。
- genvar j;
- generate for (j=0; j<4; j=j+1)begin : rxdata_bus
- assign rgmii_rxd_delay[j]=rgmii_rxd[j];
- end
- endgenerate
- // Instantiate Double Data Rate Input flip-flops.
- // DDR_CLK_EDGE attribute specifies output data alignment from IDDR component
- genvar k;
- generate for (k=0; k<4; k=k+1)begin : rxdata_in_bus
- PH1_LOGIC_IDDR #(
- .PIPEMODE ("PIPED")
- )
- rgmii_rx_data_in
- (
- .q0 (gmii_rxd_reg[k]),
- .q1 (gmii_rxd_reg[k+4]),
- .clk (rgmii_rxc_int),
- .d (rgmii_rxd_delay[k]),
- .rst (1'b0)
- );
-
- end
- endgenerate
-
- //---------------------------------------------------------------------------
- // RGMII 接收逻辑:rgmii_rx_ctl ------> gmii_rx_dv、gmii_rx_er
- //---------------------------------------------------------------------------
- assign rgmii_rx_ctl_delay = rgmii_rx_ctl;
-
- PH1_LOGIC_IDDR #(
- .PIPEMODE ("PIPED")
- )
- rgmii_rx_ctl_in
- (
- .q0 (gmii_rx_dv_reg),
- .q1 (rgmii_rx_ctl_reg),
- .clk (rgmii_rxc_int),
- .d (rgmii_rx_ctl_delay),
- .rst (1'b0)
- );
- // 解码 gmii_rx_er signal
- assign gmii_rx_er_reg = gmii_rx_dv_reg ^ rgmii_rx_ctl_reg;
- //----------------------------------------------------------------------------
- // 接收逻辑:内部信号给到输出端口:gmii_rxd、gmii_rx_dv、gmii_rx_er、gmii_col、gmii_crs
- //----------------------------------------------------------------------------
- always @ (posedge gmii_rx_clk )begin
- gmii_rxd <= gmii_rxd_reg;
- gmii_rx_dv <= gmii_rx_dv_reg;
- gmii_rx_er <= gmii_rx_er_reg;
- end
- // 从RGMII创建GMII格式的冲突和载波侦听信号
- assign gmii_col = (gmii_tx_en | gmii_tx_er) & (gmii_rx_dv_reg | gmii_rx_er_reg);
- assign gmii_crs = (gmii_tx_en | gmii_tx_er) | (gmii_rx_dv_reg | gmii_rx_er_reg);
- //==============================================================================
- // RGMII 状态寄存器
- //==============================================================================
- // 在帧间间隔期间启用带内状态寄存器
- assign inband_ce = !(gmii_rx_dv_reg || gmii_rx_er_reg);
- always @ (posedge gmii_rx_clk or posedge gmii_rx_reset)begin
- if (gmii_rx_reset) begin
- link_status <= 1'b0;
- clock_speed[1:0] <= 2'b0;
- duplex_status <= 1'b0;
- end
- else if (inband_ce) begin
- link_status <= gmii_rxd_reg[0];
- clock_speed[1:0] <= gmii_rxd_reg[2:1];
- duplex_status <= gmii_rxd_reg[3];
- end
- end
- endmodule
复制代码 3.3 数据转换模块设计rgmii_interface模块只实现了千兆速率下的RGMII接口收发,而100Mps和10Mps速率的采样时钟分别为25MHz和2.5MHz,数据单沿传输。100M和10M以太网的内部逻辑时钟为12.5MHz和1.25MHz,数据位宽为8位,这就需要额外设计一个模块进行跨时钟域处理,并且实现8bit数据和4bit数据的相互转换,称为rgmii_cdc模块。
rgmii_cdc模块的设计思路与mac层类似,在发送端和接收端各使用两个异步FIFO,一个用来缓存数据,即data_fifo,一个用来缓存长度队列,即len_fifo。对写入data_fifo的数据数量进行计数,当写入最后一个数据时,将计数的值也写入len_fifo中。当len_fifo为非空时,代表有一帧数据等待读出,这时候将缓存的长度信息读取出来,然后通过计数器读取出一个完的帧。
(, 下载次数: 46)
rgmii_cdc模块的代码如下:
- module rgmii_cdc
- (
- input wire speed_10_100 ,
- input wire I_rst ,
- input wire I_gmii_rclk ,
- input wire I_gmii_rvalid ,
- input wire [7:0] I_gmii_rdata ,
- input wire I_gmii_tclk ,
- output reg O_gmii_tvalid ,
- output reg [7:0] O_gmii_tdata ,
- input wire I_rx_clk ,
- output reg O_rx_valid ,
- output reg [7:0] O_rx_data ,
- input wire I_tx_clk ,
- input wire I_tx_valid ,
- input wire [7:0] I_tx_data
- );
- reg [7:0] tx_gap;
- reg [15:0] tx_len_fifo_din;
- wire tx_len_fifo_wren;
- wire tx_len_fifo_rdemtpy;
- reg tx_len_fifo_rden;
- wire [15:0] tx_len_fifo_dout;
- reg tx_data_fifo_rden;
- wire [7:0] tx_data_fifo_dout;
- reg [15:0] tx_len;
- reg tx_valid_reg;
- reg tcnt_10_100;
- reg [15:0] tx_cnt;
- reg tx_lock;
- reg tx_run;
- reg tx_en;
- assign tx_len_fifo_wren = tx_valid_reg && ~I_tx_valid;
- always@(posedge I_tx_clk)
- tx_valid_reg <= I_tx_valid;
- always@(posedge I_tx_clk or posedge I_rst) begin
- if(I_rst)
- tx_len_fifo_din <= 16'd0;
- else if(I_tx_valid)
- tx_len_fifo_din <= tx_len_fifo_din + 1'b1;
- else
- tx_len_fifo_din <= 16'd0;
- end
- udp_pkg_buf #(
- .DATA_WIDTH_W (8 ),
- .DATA_WIDTH_R (8 ),
- .ADDR_WIDTH_W (12 ),
- .ADDR_WIDTH_R (12 ),
- .SHOW_AHEAD_EN (1'b1 ),
- .OUTREG_EN ("NOREG" )
- )
- tx_data_fifo
- (
- .rst (I_rst ),
- .clkw (I_tx_clk ),
- .we (I_tx_valid ),
- .di (I_tx_data ),
- .clkr (I_gmii_tclk ),
- .re (tx_data_fifo_rden ),
- .dout (tx_data_fifo_dout ),
- .wrusedw (),
- .rdusedw ()
- );
- udp_pkg_buf #(
- .DATA_WIDTH_W (16 ),
- .DATA_WIDTH_R (16 ),
- .ADDR_WIDTH_W (6 ),
- .ADDR_WIDTH_R (6 ),
- .SHOW_AHEAD_EN (1'b1 ),
- .OUTREG_EN ("NOREG" )
- )
- tx_len_fifo
- (
- .rst (I_rst ),
- .clkw (I_tx_clk ),
- .we (tx_len_fifo_wren ),
- .di (tx_len_fifo_din ),
- .clkr (I_gmii_tclk ),
- .re (tx_len_fifo_rden ),
- .dout (tx_len_fifo_dout ),
- .wrusedw (),
- .rdusedw (),
- .empty_flag (tx_len_fifo_rdemtpy)
- );
- always@(posedge I_gmii_tclk or posedge I_rst) begin
- if(I_rst)
- tx_lock <= 1'b0;
- else if(!tx_lock && !tx_len_fifo_rdemtpy)
- tx_lock <= 1'b1;
- else if(tx_cnt == 16'd0 && tx_gap == 8'd10 && ~speed_10_100)
- tx_lock <= 1'b0;
- else if(tx_cnt == 16'd0 && tx_gap == 8'd22 && speed_10_100)
- tx_lock <= 1'b0;
- else
- tx_lock <= tx_lock;
- end
- always@(posedge I_gmii_tclk or posedge I_rst) begin
- if(I_rst)
- tx_len_fifo_rden <= 1'b0;
- else if(!tx_lock && !tx_len_fifo_rdemtpy)
- tx_len_fifo_rden <= 1'b1;
- else
- tx_len_fifo_rden <= 1'b0;
- end
- always@(posedge I_gmii_tclk or posedge I_rst) begin
- if(I_rst)
- tx_len <= 16'd0;
- else if(tx_len_fifo_rden)
- tx_len <= tx_len_fifo_dout;
- else
- tx_len <= tx_len;
- end
- always@(posedge I_gmii_tclk or posedge I_rst) begin
- if(I_rst)
- tx_run <= 1'b0;
- else if(tx_lock && tx_len_fifo_rden)
- tx_run <= 1'b1;
- else if(tx_cnt == tx_len - 1'b1 && ~speed_10_100)
- tx_run <= 1'b0;
- else if(tx_cnt == tx_len - 1'b1 && tcnt_10_100 && speed_10_100)
- tx_run <= 1'b0;
- else
- tx_run <= tx_run;
- end
- always@(posedge I_gmii_tclk or posedge I_rst) begin
- if(I_rst)
- tx_data_fifo_rden <= 1'b0;
- else if(tcnt_10_100 && tx_run)
- tx_data_fifo_rden <= 1'b1;
- else if(speed_10_100)
- tx_data_fifo_rden <= 1'b0;
- else if(tx_lock && tx_len_fifo_rden && ~speed_10_100)
- tx_data_fifo_rden <= 1'b1;
- else if(tx_cnt >= tx_len - 1'b1)
- tx_data_fifo_rden <= 1'b0;
- else
- tx_data_fifo_rden <= tx_data_fifo_rden;
- end
- always@(posedge I_gmii_tclk or posedge I_rst) begin
- if(I_rst)
- tcnt_10_100 <= 1'b0;
- else if(tx_run && speed_10_100)
- tcnt_10_100 <= tcnt_10_100 + 1'b1;
- else
- tcnt_10_100 <= 1'b0;
- end
- always@(posedge I_gmii_tclk or posedge I_rst) begin
- if(I_rst)
- tx_gap <= 8'd0;
- else if(~speed_10_100 && tx_gap == 8'd10)
- tx_gap <= 8'd0;
- else if(speed_10_100 && tx_gap == 8'd22)
- tx_gap <= 8'd0;
- else if((tx_lock && tx_cnt == tx_len - 1'b1) || tx_gap > 8'd0)
- tx_gap <= tx_gap + 1'b1;
- end
- always@(posedge I_gmii_tclk or posedge I_rst) begin
- if(I_rst)
- tx_cnt <= 16'd0;
- else if(tx_cnt == tx_len)
- tx_cnt <= 16'd0;
- else if(tx_data_fifo_rden)
- tx_cnt <= tx_cnt + 1'b1;
- else
- tx_cnt <= tx_cnt;
- end
- always@(posedge I_gmii_tclk or posedge I_rst) begin
- if(I_rst)
- tx_en <= 1'b0;
- else if(tx_run && speed_10_100)
- tx_en <= 1'b1;
- else if(~tx_run)
- tx_en <= 1'b0;
- end
- always@(posedge I_gmii_tclk or posedge I_rst) begin
- if(I_rst)
- O_gmii_tvalid <= 1'b0;
- else if(~speed_10_100)
- O_gmii_tvalid <= tx_data_fifo_rden;
- else if(speed_10_100 && ~tx_en)
- O_gmii_tvalid <= 1'b0;
- else if(speed_10_100 && tx_en)
- O_gmii_tvalid <= 1'b1;
- else
- O_gmii_tvalid <= 1'b0;
- end
- always@(posedge I_gmii_tclk or posedge I_rst) begin
- if(I_rst)
- O_gmii_tdata <= 8'd0;
- else if(tx_run && ~speed_10_100)
- O_gmii_tdata <= tx_data_fifo_dout;
- else if(tx_en && speed_10_100)
- O_gmii_tdata <= tcnt_10_100 ? {4'd0,tx_data_fifo_dout[3:0]} : {4'd0,tx_data_fifo_dout[7:4]};
- else
- O_gmii_tdata <= 8'd0;
- end
- //==================================================================
- //==================================================================
- reg [15:0] rx_len_fifo_din;
- wire rx_len_fifo_wren;
- wire rx_len_fifo_rdemtpy;
- reg rx_len_fifo_rden;
- wire [15:0] rx_len_fifo_dout;
- reg [7:0] rx_data_fifo_din;
- reg rx_data_fifo_wren;
- reg rx_data_fifo_rden;
- wire [7:0] rx_data_fifo_dout;
- reg gmii_rvalid_reg;
- reg [15:0] rx_len;
- reg [15:0] rx_cnt;
- reg rx_lock;
- reg rcnt_10_100;
- assign rx_len_fifo_wren = gmii_rvalid_reg && ~I_gmii_rvalid;
- always@(posedge I_gmii_rclk)
- gmii_rvalid_reg <= I_gmii_rvalid;
- always@(posedge I_gmii_rclk or posedge I_rst) begin
- if(I_rst)
- rx_len_fifo_din <= 0;
- else if(~I_gmii_rvalid)
- rx_len_fifo_din <= 0;
- else if(I_gmii_rvalid && ~speed_10_100)
- rx_len_fifo_din <= rx_len_fifo_din + 1;
- else if(I_gmii_rvalid && speed_10_100 && rcnt_10_100)
- rx_len_fifo_din <= rx_len_fifo_din + 1;
- else
- rx_len_fifo_din <= rx_len_fifo_din;
- end
- always@(posedge I_gmii_rclk or posedge I_rst) begin
- if(I_rst)
- rcnt_10_100 <= 1'b0;
- else if(I_gmii_rvalid && speed_10_100)
- rcnt_10_100 <= rcnt_10_100 + 1'b1;
- else
- rcnt_10_100 <= 1'b0;
- end
- always@(posedge I_gmii_rclk or posedge I_rst) begin
- if(I_rst)
- rx_data_fifo_wren <= 1'b0;
- else if(~speed_10_100)
- rx_data_fifo_wren <= I_gmii_rvalid;
- else if(speed_10_100)
- rx_data_fifo_wren <= rcnt_10_100;
- end
- always@(posedge I_gmii_rclk or posedge I_rst) begin
- if(I_rst)
- rx_data_fifo_din <= 8'd0;
- else if(speed_10_100)
- rx_data_fifo_din <= {I_gmii_rdata[3:0], rx_data_fifo_din[7:4]};
- else
- rx_data_fifo_din <= I_gmii_rdata;
- end
- udp_pkg_buf #(
- .DATA_WIDTH_W (8 ),
- .DATA_WIDTH_R (8 ),
- .ADDR_WIDTH_W (12 ),
- .ADDR_WIDTH_R (12 ),
- .SHOW_AHEAD_EN (1'b1 ),
- .OUTREG_EN ("NOREG" )
- )
- rx_data_fifo
- (
- .rst (I_rst ),
- .clkw (I_gmii_rclk ),
- .we (rx_data_fifo_wren ),
- .di (rx_data_fifo_din ),
- .clkr (I_rx_clk ),
- .re (rx_data_fifo_rden ),
- .dout (rx_data_fifo_dout ),
- .wrusedw (),
- .rdusedw ()
- );
- udp_pkg_buf #(
- .DATA_WIDTH_W (16 ),
- .DATA_WIDTH_R (16 ),
- .ADDR_WIDTH_W (6 ),
- .ADDR_WIDTH_R (6 ),
- .SHOW_AHEAD_EN (1'b1 ),
- .OUTREG_EN ("NOREG" )
- )
- rx_len_fifo
- (
- .rst (I_rst ),
- .clkw (I_gmii_rclk ),
- .we (rx_len_fifo_wren ),
- .di (rx_len_fifo_din ),
- .clkr (I_rx_clk ),
- .re (rx_len_fifo_rden ),
- .dout (rx_len_fifo_dout ),
- .wrusedw (),
- .rdusedw (),
- .empty_flag (rx_len_fifo_rdemtpy)
- );
- always@(posedge I_rx_clk or posedge I_rst) begin
- if(I_rst)
- rx_lock <= 1'b0;
- else if(!rx_lock && !rx_len_fifo_rdemtpy)
- rx_lock <= 1'b1;
- else if(rx_cnt == rx_len - 1'b1)
- rx_lock <= 1'b0;
- else
- rx_lock <= rx_lock;
- end
- always@(posedge I_rx_clk or posedge I_rst) begin
- if(I_rst)
- rx_len_fifo_rden <= 1'b0;
- else if(!rx_lock && !rx_len_fifo_rdemtpy)
- rx_len_fifo_rden <= 1'b1;
- else
- rx_len_fifo_rden <= 1'b0;
- end
- always@(posedge I_rx_clk or posedge I_rst) begin
- if(I_rst)
- rx_len <= 16'd0;
- else if(rx_len_fifo_rden)
- rx_len <= rx_len_fifo_dout;
- else
- rx_len <= rx_len;
- end
- always@(posedge I_rx_clk or posedge I_rst) begin
- if(I_rst)
- rx_data_fifo_rden <= 1'b0;
- else if(rx_lock && rx_len_fifo_rden)
- rx_data_fifo_rden <= 1'b1;
- else if(rx_cnt == rx_len - 1'b1)
- rx_data_fifo_rden <= 1'b0;
- else
- rx_data_fifo_rden <= rx_data_fifo_rden;
- end
- always@(posedge I_rx_clk or posedge I_rst) begin
- if(I_rst)
- rx_cnt <= 16'd0;
- else if(rx_cnt == rx_len - 1)
- rx_cnt <= 16'd0;
- else if(rx_data_fifo_rden)
- rx_cnt <= rx_cnt + 1'b1;
- else
- rx_cnt <= rx_cnt;
- end
- always@(posedge I_rx_clk or posedge I_rst) begin
- if(I_rst)
- O_rx_valid <= 1'b0;
- else if(rx_data_fifo_rden)
- O_rx_valid <= 1'b1;
- else
- O_rx_valid <= 1'b0;
- end
- always@(posedge I_rx_clk or posedge I_rst) begin
- if(I_rst)
- O_rx_data <= 8'd0;
- else if(rx_data_fifo_rden)
- O_rx_data <= rx_data_fifo_dout;
- else
- O_rx_data <= 8'd0;
- end
- endmodule
复制代码 4 三速以太网回环测试
4.1 100M以太网测试udp协议栈的逻辑时钟为12.5MHz,RGMII接口的时钟为25MHz,将参数speed_10_100设置成1’b1。
打开设备管理器,在网络适配器选项中选择自己的以太网卡。
(, 下载次数: 40)
在高级选项中将网卡的连接速度和工作模式设置成100Mbps全双工。
(, 下载次数: 39)
连接好网线,并将程序下栽进开发板中。此时开发板网卡自协商的速度为100Mbps。使用网络调试助手发送数据帧,FPGA将报文解析完成后,通过发送端传回PC,结果如下图所示。
(, 下载次数: 33)
4.2 10M以太网测试udp协议栈的逻辑时钟为1.25MHz,RGMII接口的时钟为2.5MHz,时钟选择由分频逻辑生成。选择125MHz时钟进行计数分配,代码如下:
- reg clk_1_25;
- reg clk_2_5;
- reg clk_2_5_90;
- reg [7:0] clk_cnt;
- always@(posedge clk_125 or negedge pll_locked) begin
- if(!pll_locked)
- clk_cnt <= 0;
- else if(clk_cnt == 99)
- clk_cnt <= 0;
- else
- clk_cnt <= clk_cnt + 1;
- end
- always@(posedge clk_125 or negedge pll_locked) begin
- if(!pll_locked)
- clk_1_25 <= 0;
- else
- case(clk_cnt)
- 49 :clk_1_25 <= 1;
- 99 :clk_1_25 <= 0;
- default:clk_1_25 <= clk_1_25;
- endcase
- end
- always@(posedge clk_125 or negedge pll_locked) begin
- if(!pll_locked)
- clk_2_5 <= 0;
- else
- case(clk_cnt)
- 24 :clk_2_5 <= 1;
- 49 :clk_2_5 <= 0;
- 74 :clk_2_5 <= 1;
- 99 :clk_2_5 <= 0;
- default:clk_2_5 <= clk_2_5;
- endcase
- end
- always@(posedge clk_125 or negedge pll_locked) begin
- if(!pll_locked)
- clk_2_5_90 <= 0;
- else
- case(clk_cnt)
- 12 :clk_2_5_90 <= 1;
- 37 :clk_2_5_90 <= 0;
- 62 :clk_2_5_90 <= 1;
- 87 :clk_2_5_90 <= 0;
- default:clk_2_5_90 <= clk_2_5_90;
- endcase
- end
复制代码将网卡的连接速度和工作模式设置成10Mbps全双工。上电开发板,PC设置中显示网络连接速度为10Mbps。
(, 下载次数: 37)
将程序下栽进开发板中,通过网络调试助手进行数据回环,结果如下图所示。
(, 下载次数: 32)
ping包测试结果如下图所示。
(, 下载次数: 32)
欢迎光临 FPGA+AI工程师技术社区 (https://www.uisrc.com/) |
Powered by Discuz! X3.5 |