• FPGA控制W5500完成UDP环回测试


    1 前言

    本文针对已经对W5500有一定了解,并且数据手册已经通读一遍的人群,因为博主目前只完成了UDP环回测试,因此在后文可能只介绍有关UDP部分。

    2 前期准备

    1.FPGA核心板或者开发板;
    2.W5500模块。下图是博主使用的模块;
    在这里插入图片描述
    3.网络调试助手,网上随便找一个就行;

    3 W5500寄存器描述

    主机与W5500通信有固定协议(数据帧),主机先发两个字节的寄存器地址,然后一个字节的控制字,最后是数据,这个数据可以是一个字节,也可以是N的字节,但是W5000为了方便操作,可以将这个N分为1、2、4和可变长度,这些都可以配置。下图是数据帧格式。
    在这里插入图片描述
    下图是控制字段寄存器,BSB4~BSB0选择寄存器,RWB是读写选择位(1:写 0:读),OM选择数据段中N的字节数。
    在这里插入图片描述

    W5500的寄存器分为两种,一是通用寄存器,二是socket寄存器。这两种寄存器通过数据帧中的地址段来选择,如下图所示。例如,当寄存器地址为16’h0000时,如果BSB是5‘h00000,那么此时选择的是通用寄存器中的MR寄存器;如果BSB是5‘h00001,那么此时选择的是socket0寄存器中的Sn_MR寄存器。
    在这里插入图片描述

    4 W5500 环回测试

    4.1 W5500初始化

    4.1.1 通用寄存器初始化

    通用寄存器的初始化就是配置源网关、子网掩码、MAC地址,IP地址、PHY寄存器,然后清中断。

    always@(posedge clk,negedge rst_n)
    	if(!rst_n)
    		o_dat<='d0;
    	else begin
    		case(state)
    			WR_MR://WRMR_CMD,
    				o_dat<=8'h00;
    			WRGAR_CMD,WR_GAR:
    				if(rdreq)
    					case(cnt_byte)
    							'd00:o_dat<=GAR[31:24];
    							'd01:o_dat<=GAR[23:16];
    							'd02:o_dat<=GAR[15:08];
    							'd03:o_dat<=GAR[07:00];
    						default:;
    					endcase
    				else
    					o_dat<=o_dat;
    			WR_SUBR://WRSUBR_CMD,
    				if(rdreq)
    					case(cnt_byte)
    							'd00:o_dat<=SUBR[31:24];
    							'd01:o_dat<=SUBR[23:16];
    							'd02:o_dat<=SUBR[15:08];
    							'd03:o_dat<=SUBR[07:00];
    						default:;
    					endcase
    				else
    					o_dat<=o_dat;
    			WR_SHAR://WRSHAR_CMD,
    				if(rdreq)
    					case(cnt_byte)
    							'd00:o_dat<=SHAR[47:40];
    							'd01:o_dat<=SHAR[39:32];
    							'd02:o_dat<=SHAR[31:24];
    							'd03:o_dat<=SHAR[23:16];
    							'd04:o_dat<=SHAR[15:08];
    							'd05:o_dat<=SHAR[07:00];							
    						default:;
    					endcase
    				else
    					o_dat<=o_dat;	
    			WR_IP://WRIP_CMD,
    				if(rdreq)
    					case(cnt_byte)
    							'd00:o_dat<=SIPR[31:24];
    							'd01:o_dat<=SIPR[23:16];
    							'd02:o_dat<=SIPR[15:08];
    							'd03:o_dat<=SIPR[07:00];
    						default:;
    					endcase
    				else
    					o_dat<=o_dat;
    			WRIR_CMD,WR_IR,WRIMR_CMD,WR_IMR:
    				o_dat<=8'hFF;
    			WR_RTR://WRRTR_CMD,
    				if(rdreq)
    					case(cnt_byte)
    							'd00:o_dat<=8'h07;
    							'd01:o_dat<=8'hD0;
    						default:;
    					endcase
    				else
    					o_dat<=o_dat;
    			WRRCR_CMD,WR_RCR:
    				o_dat<=8'h08;
    			WRPHY_CMD,WR_PHY:
    				o_dat<=8'b11111111;
    			default:o_dat<=8'h00;
    		endcase
    	end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71

    4.1.2 socket寄存器初始化

    socket寄存器配置跟通用寄存器类似,先配置目的socket模式(TCP、UDP、MACRAW)、MAC地址、目的IP地址、目的端口、以及本地端口等寄存器,然后清中断等,最后配置Sn_CR寄存器打开端口,之后就是定时查询SN_SR寄存器,等待socket初始化成功(Sn_SR寄存器值为8’h22)。

    always@(posedge clk,negedge rst_n)
    	if(!rst_n)
    		o_dat<='d0;	
    	else begin
    		case(state)
    			WR_MR:
    				o_dat<=8'h02;
    			WR_IR,WR_IMR:
    				o_dat<=8'hFF;
    			WR_PORT:
    				if(rdreq)
    					case(cnt_byte)
    						'd00:o_dat<=SN_PORT[15:08];		
    						'd01:o_dat<=SN_PORT[07:00];
    						default:;
    					endcase
    				else
    					o_dat<=o_dat;
    			WR_DHAR:
    				if(rdreq)
    					case(cnt_byte)
    						'd00:o_dat<=SN_DSHAR[47:40];		
    						'd01:o_dat<=SN_DSHAR[39:32];
    						'd02:o_dat<=SN_DSHAR[31:24];		
    						'd03:o_dat<=SN_DSHAR[23:16];
    						'd04:o_dat<=SN_DSHAR[15:08];		
    						'd05:o_dat<=SN_DSHAR[07:00];							
    						default:;
    					endcase
    				else
    					o_dat<=o_dat;
    			WR_DIPR:
    				if(rdreq)
    					case(cnt_byte)
    						'd00:o_dat<=SN_DIP[31:24];		
    						'd01:o_dat<=SN_DIP[23:16];
    						'd02:o_dat<=SN_DIP[15:08];		
    						'd03:o_dat<=SN_DIP[07:00];						
    						default:;
    					endcase
    				else
    					o_dat<=o_dat;
    			WR_DPORT:
    				if(rdreq)
    					case(cnt_byte)
    						'd00:o_dat<=SN_DPORT[15:08];		
    						'd01:o_dat<=SN_DPORT[07:00];
    						default:;
    					endcase
    				else
    					o_dat<=o_dat;
    			WR_MSSR:
    				if(rdreq)
    					case(cnt_byte)
    						'd00:o_dat<=8'h05;		
    						'd01:o_dat<=8'hB4;
    						default:;
    					endcase
    				else
    					o_dat<=o_dat;		
    			WR_CR:
    				o_dat<=8'h01;		
    			default:;
    		endcase	
    	end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    4.2 W5500数据接收

    软件一直在查询Sn_RX_RSR寄存器(Socket n 空闲接收缓存寄存器),显示了 Socket n 接收缓存中已接收和保存的数据大小,当其值大于0时,表明socket已经接收到数据,因此可以进行数据接收流程。W5500数据手册提供了一种数据读取的方法,如下图所示。
    在这里插入图片描述
    socket的接收缓存(RX_BUF)有两个指针,一是写指针(Sn_RX_WR)二是读指针(Sn_RX_RD),当外部将UDP数据发送给W5500时,Sn_RX_WR会自动增加,因此Sn_RX_WR是W5500芯片控制的。Sn_RX_RD由用户控制,控制流程如上图所示。

    always@(posedge clk,negedge rst_n)
    	if(!rst_n)
    		o_dat<='d0;
    	else begin
    		case(state)
    			WR_RXRD:	
    				if(rdreq)
    					case(cnt_byte)
    						'd00:o_dat<=rx_ptr[15:08];		
    						'd01:o_dat<=rx_ptr[07:00];
    						default:;
    					endcase
    				else
    					o_dat<=o_dat;
    			WR_CR:
    				o_dat<=8'h40;		
    			default:o_dat<=o_dat;
    		endcase
    	end
    	
    always@(posedge clk,negedge rst_n)
    	if(!rst_n)
    		rx_ptr<='d0;
    	else begin
    		case(state)
    				RD_RXRD:
    					if(rdrxrd_vld)
    						rx_ptr<=dinr;
    					else
    						rx_ptr<=rx_ptr;
    				RD_RXBUF:
    					if(den)
    						rx_ptr<=rx_ptr+'d1;
    					else
    						rx_ptr<=rx_ptr;
    //				END:
    //					rx_ptr<='d0;
    			default:rx_ptr<=rx_ptr;
    		endcase	
    	end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    4.3 W5500数据发送

    软件进入数据发送流程时,先查询W5500的发送缓存剩余空间大小,如果剩余空间大于用户发送数据长度,那么继续后续流程,反之则拒绝发送用户数据。同样,手册也提供了一种数据发送的方法,如下图所示。
    在这里插入图片描述

    4.4 数据环回

    软件例化例一个ram来存储收到的数据,接收完成后将数据读出然后发送,代码如下:

    always@(posedge clk,negedge rst_n)
    	if(!rst_n)
    		state<='d0;
    	else begin
    		case(state)
    			IDLE:
    				if(rxdat_end && waddr>'d0)
    					state<=RDDAT_PRE;
    				else
    					state<=IDLE;
    			RDDAT_PRE:
    				state<=RD_DAT;
    			RD_DAT:
    				if(dat_tx_end)
    					state<=END;
    				else 
    					state<=RD_DAT;
    			END:state<=IDLE;
    			default:state<=IDLE;
    		endcase
    	end
    
    always@(posedge clk,negedge rst_n)
    	if(!rst_n)
    		dat_len<='d0;
    	else if(state==RDDAT_PRE)
    		dat_len<=waddr;
    
    always@(posedge clk,negedge rst_n)
    	if(!rst_n)
    		o_dat_tx_req<='d0;
    	else	if(state==RDDAT_PRE)
    		o_dat_tx_req<='d1;
    	else	if(state==END)
    		o_dat_tx_req<='d0;	
    	else
    		o_dat_tx_req<=o_dat_tx_req;
    		
    
    always@(posedge clk,negedge rst_n)
    	if(!rst_n)
    		waddr<='d0;		
    	else if(rxdat_vld)
    		waddr<=waddr+'d1;
    	else if(state==END)	
    		waddr<='d0;	
    	
    always@(posedge clk,negedge rst_n)
    	if(!rst_n)
    		raddr<='d0;		
    	else if(dat_tx_rden)
    		raddr<=raddr+'d1;
    	else if(state==END)	
    		raddr<='d0;			
    		
    my_ram	
    	my_ram_inst (
    			.clock 			( clk 			),
    			.wren 			( rxdat_vld		),
    			.wraddress 		( waddr 		),
    			.data 			( rxdat 		),
    			
    			.rden 			( dat_tx_rden 	),
    			.rdaddress 		( raddr 		),
    			.q 				( o_dat 		)
    	);
    			
    assign 	o_dat_len	=dat_len;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    最后测试结果如下图所示,包含wireshark抓包结果。
    Alt

    5 总结

    W5500的UDP通信是不难的,只要初始化正确,然后在收发时正确读写socket寄存器,然后就没啥难度了,手册的话还是要多看几遍,博主是在官网下载中文手册, W5500官网(手册、参考电路、驱动固件、例程等),完整代码及工程放在评论区。
    博主在完成UDP环回测试后还尝试进行TCP测试验证,W5500作为客户端,但是在配置完成后发现W5500都没有发出ARP包,后来就没有测了,暂时先放下。

  • 相关阅读:
    http协议浅分析
    Java内部类(清晰明了!)
    TVP专家谈腾讯云 Cloud Studio:开启云端开发新篇章
    k8s KubeSphere流水线部署SpringBoot后端项目 详细教程
    图的广度优先遍历
    牛客-- 求解立方根python
    java文件命令行报错: 找不到或无法加载主类XXX报错及解决
    Apple developer证书、标识符和描述文件
    【JVS低代码开发平台】支持纯手工配置的数据加工、处理、展现的数据仓库
    sql 多表 表与表之间的关系,多表查询
  • 原文地址:https://blog.csdn.net/changshengxiao/article/details/128124140