FPGA千兆以太网UDP收发状态机实现

关注、星标公众号,精彩内容每日送达
来源:网络素材

一.UDP发送模块

1.1模块框图

接口描述:

//输入接口:

gmii_clk:发送时钟。

sys_rst_n:系统复位。

send_en:发送使能。

send_data:发送的数据,位宽为32位。

send_data_num:发送数据的字节数,单位是byte。

crc_data/crc_next:crc检验数据。

//输出接口:

send_end:发送结束使能。

read_data_req:fifo读数据请求。

eth_tx_en:发送字节数据有效信号。

eth_tx_data:发送的数据,单位是字节。

crc_en/crc_clr:crc使能和清空信号。

1.2.波形图



1.3逻辑实现思路

开始状态是空闲状态;当trig_tx_en信号为高电平时,进入CHECK_SUM状态,在这个状态进行IP_UDP校验,当计数器cnt计数到3的时候,跳转到下一个状态,PACKET_HEAD,在这这个状态下进行以太网帧头的发送。由于发送的帧头为七个8‘h55加一个8’hd5,所以计数器计数到7时,跳转到下一个状态EHT_HEAD,在这个状态下,发送的数据为目的地址(6个个字节),源地址(6个字节),加两个字节的ETH类型。所以计数到13时跳转到下一个IP_UDP_HEAD状态。在IP_UDP_HEAD状态下,发送IP_UDP头部,以4个字节为单位,一个7个四个字节,所以帧计数器frame_cnt计数到6,这时候跳转到SEND_DATA状态,在这个状态下,data_cnt为实际发送数据的计数值,又设置变量send_data_len,此变量为实际要发送的数据值,值得注意的是,又上一篇博客可知,以太网数据发送部分最低为46个字节,而IP_UDP头部数据就占用了28个字节,所以带发送的数据最少为18个字节,为了满足实际情况的合理性,故考虑实际发送的数据少于18个字节的情况,设置add_cnt变量,此变量为少于18个最小字节的,待补充字节的个数。当add_cnt和data_cnt两者相加的值等于send_data_len-1时,状态跳转到CRC状态,在此状态接受四个字节的crc校验数据,cnt等于3时,跳转到IDLE状态,至此一帧的以太网数据发送完成。

1.4 发送状态机代码

  1. module udp_tx

  2. #(

  3.     parameter   BOARD_MAC   = 48'hff_ff_ff_ff_ff_ff ,

  4.     parameter   BOARD_IP    = 32'hff_ff_ff_ff       ,

  5.     parameter   BOARD_PORT  = 16'd1234              ,

  6.     parameter   PC_MAC      = 48'hff_ff_ff_ff_ff_ff ,

  7.     parameter   PC_IP       = 32'hff_ff_ff_ff       ,

  8.     parameter   PC_PORT     = 16'd1234                 

  9. )

  10. (

  11. input wire [31:0] crc_data ,

  12. input wire [7:0] crc_next ,

  13. input wire [15:0] tx_byte_num ,

  14. input wire [31:0] tx_data ,

  15. input wire clk ,

  16. input wire rst_n ,

  17. input wire tx_start_en ,

  18. output wire [7:0] gmii_txd ,

  19. output wire crc_clr ,

  20. output wire crc_en ,

  21. output wire gmii_tx_en ,

  22. output wire tx_done ,

  23. output wire tx_req

  24. );

  25. reg tx_start_en_d0;

  26. reg tx_start_en_d1;

  27. wire pos_start_en;

  28. reg  trig_tx_en;

  29. assign pos_start_en = ~tx_start_en_d1 & tx_start_en_d0;

  30. always@(posedge clk or negedge rst_n)begin

  31. if(!rst_n) begin

  32. tx_start_en_d0 <= 'd0;

  33. tx_start_en_d1 <= 'd0;

  34. end

  35. else begin

  36. tx_start_en_d0 <= tx_start_en;

  37. tx_start_en_d1 <= tx_start_en_d0;

  38. end 

  39. end

  40. always @(posedge clk or negedge rst_n) begin

  41. if(!rst_n)

  42. trig_tx_en <= 'd0;

  43. else 

  44. trig_tx_en <= pos_start_en;

  45. end

  46. //状态变量

  47. reg [6:0] cur_state;

  48. reg [6:0] next_state;

  49. localparam  IDLE        = 7'b0_000_001,

  50.             CHECK_SUM   = 7'b0_000_010,

  51.             PACKET_HEAD = 7'b0_000_100,

  52.             ETH_HEAD    = 7'b0_001_000,

  53.             IP_UDP_HEAD = 7'b0_010_000,

  54.             SEND_DATA   = 7'b0_100_000,

  55.             CRC         = 7'b1_000_000;

  56. localparam  ETH_TYPE    =  16'h0800   ;          

  57. //数据长度变量

  58. reg [31:0] check_sum ;

  59. reg [15:0] data_len ;

  60. reg [15:0] ip_len ;

  61. reg [15:0] udp_len ;

  62. //状态转移变量

  63. reg sw_en ;

  64. //计数变量

  65. reg [4:0] cnt ;

  66. reg [2:0] frame_cnt ;

  67. reg [15:0] data_cnt ;

  68. reg [4:0] cnt_add ;

  69. reg [1:0] cnt_send_state ;

  70. //寄存器输出变量,防止时序违例

  71. reg [7:0] r_gmii_txd ;

  72. reg r_crc_clr ;

  73. reg r_crc_en ;

  74. reg r_gmii_tx_en ;

  75. reg r_tx_done ;

  76. reg r_tx_req ;

  77. assign gmii_txd  = r_gmii_txd ;

  78. assign crc_clr  = r_crc_clr ;

  79. assign crc_en  = r_crc_en ;

  80. assign gmii_tx_en = r_gmii_tx_en ;

  81. assign tx_done  = r_tx_done ;

  82. assign tx_req  = r_tx_req ;

  83. //存以太网数据变量

  84. reg     [7:0]   packet_head[7:0]    ;   

  85. reg     [7:0]   eth_head[13:0]      ;   

  86. reg     [31:0]  ip_udp_head[6:0]    ;   

  87. wire [15:0] send_data_len ;

  88. assign send_data_len = (data_len >= 16'd18)?data_len : 16'd18;

  89. //时序逻辑表示状态寄存

  90. always@(posedge clk or negedge rst_n)begin

  91. if(!rst_n)

  92. cur_state <= IDLE;

  93. else

  94. cur_state <= next_state;

  95. end

  96. //组合逻辑表示状态转移

  97. always@(*) begin

  98. next_state = IDLE;

  99. case(cur_state)

  100. IDLE:begin

  101. if(sw_en)

  102. next_state <= CHECK_SUM;

  103. else 

  104. next_state <= IDLE;

  105. end

  106. CHECK_SUM:begin

  107. if(sw_en)

  108. next_state <= PACKET_HEAD;

  109. else 

  110. next_state <= CHECK_SUM;

  111. end

  112. PACKET_HEAD:begin

  113. if(sw_en)

  114. next_state <= ETH_HEAD;

  115. else 

  116. next_state <= PACKET_HEAD;

  117. end

  118. ETH_HEAD:begin

  119. if(sw_en)

  120. next_state <= IP_UDP_HEAD;

  121. else 

  122. next_state <= ETH_HEAD;

  123. end

  124. IP_UDP_HEAD:begin

  125. if(sw_en)

  126. next_state <= SEND_DATA;

  127. else 

  128. next_state <= IP_UDP_HEAD;

  129. end

  130. SEND_DATA:begin

  131. if(sw_en)

  132. next_state <= CRC;

  133. else 

  134. next_state <= SEND_DATA;

  135. end

  136. CRC:begin

  137. if(sw_en)

  138. next_state <= IDLE;

  139. else 

  140. next_state <= CRC;

  141. end

  142. default:next_state <= IDLE;

  143. endcase

  144. end

  145. //时序逻辑表示状态输出

  146. always@(posedge clk or negedge rst_n) begin

  147. if(!rst_n)begin

  148. check_sum <= 'd0;

  149. data_len   <= 'd0;

  150. ip_len     <= 'd0;

  151. udp_len   <= 'd0;

  152. sw_en <= 'd0;

  153. cnt <= 'd0;

  154. frame_cnt <= 'd0;

  155. data_cnt <= 'd0;

  156. cnt_add <= 'd0;

  157. cnt_send_state <= 'd0;

  158. packet_head[0] <= 'd0;

  159. packet_head[1] <= 'd0;

  160. packet_head[2] <= 'd0;

  161. packet_head[3] <= 'd0;

  162. packet_head[5] <= 'd0;

  163. packet_head[6] <= 'd0;

  164. packet_head[7] <= 'd0;

  165.         eth_head[0]  <= 'd0;

  166.         eth_head[1]  <= 'd0;

  167.         eth_head[2]  <= 'd0;

  168.         eth_head[3]  <= 'd0;

  169.         eth_head[4]  <= 'd0;

  170.         eth_head[5]  <= 'd0;

  171.         eth_head[6]  <= 'd0;

  172.         eth_head[7]  <= 'd0;

  173.         eth_head[8]  <= 'd0;

  174.         eth_head[9]  <= 'd0;

  175.         eth_head[10] <= 'd0;

  176.         eth_head[11] <= 'd0;

  177.         eth_head[12] <= 'd0;

  178.         eth_head[13] <= 'd0;

  179. ip_udp_head[1][31:16] <= 'd0;

  180. r_crc_en   <= 'd0;

  181. r_gmii_txd <= 'd0;

  182. r_gmii_tx_en <= 'd0;

  183. end

  184. else begin

  185. //initial PACKET_HEAD

  186. packet_head[0] <= 8'h55;

  187.         packet_head[1] <= 8'h55;

  188.         packet_head[2] <= 8'h55;

  189.         packet_head[3] <= 8'h55;

  190.         packet_head[4] <= 8'h55;

  191.         packet_head[5] <= 8'h55;

  192.         packet_head[6] <= 8'h55;

  193.         packet_head[7] <= 8'hd5;    

  194. //initial ETH_HEAD

  195. eth_head[0]  <= PC_MAC[47:40]       ;

  196.         eth_head[1]  <= PC_MAC[39:32]       ;

  197.         eth_head[2]  <= PC_MAC[31:24]       ;

  198.         eth_head[3]  <= PC_MAC[23:16]       ;

  199.         eth_head[4]  <= PC_MAC[15:8]        ;

  200.         eth_head[5]  <= PC_MAC[7:0]         ;

  201.         eth_head[6]  <= BOARD_MAC[47:40]    ;

  202.         eth_head[7]  <= BOARD_MAC[39:32]    ;

  203.         eth_head[8]  <= BOARD_MAC[31:24]    ;

  204.         eth_head[9]  <= BOARD_MAC[23:16]    ;

  205.         eth_head[10] <= BOARD_MAC[15:8]     ;

  206.         eth_head[11] <= BOARD_MAC[7:0]      ;

  207.         eth_head[12] <= ETH_TYPE[15:8]      ;

  208.         eth_head[13] <= ETH_TYPE[7:0]       ; 

  209. //清楚变量锁存

  210. sw_en <= 1'b0;

  211. r_tx_req <= 1'b0;

  212. r_tx_done <= 1'b0; 

  213. case(next_state)

  214. IDLE:begin

  215. r_gmii_tx_en <= 1'b0;

  216. if(pos_start_en)begin

  217. data_len <= tx_byte_num;

  218. udp_len <= tx_byte_num + 'd8;

  219. ip_len <= tx_byte_num + 'd28;

  220. end

  221. else if(trig_tx_en) begin

  222. sw_en <= 1'b1;

  223. ip_udp_head[0] <= {8'h45,8'h00,ip_len};

  224. ip_udp_head[1][31:16] <= ip_udp_head[1][31:16] + 1'b1;   

  225. ip_udp_head[1][15:0] <= 16'h4000;

  226. ip_udp_head[2] <= {8'h40,8'd17,16'h0};

  227. ip_udp_head[3] <= BOARD_IP;

  228. ip_udp_head[4] <= PC_IP;

  229. ip_udp_head[5] <= {BOARD_PORT,PC_PORT};

  230. ip_udp_head[6] <= {udp_len,16'h0000};               

  231. end

  232. end

  233. CHECK_SUM:begin

  234. cnt <= cnt + 1'd1;

  235. if(cnt == 'd0)

  236. check_sum <= ip_udp_head[0][31:16] + ip_udp_head[0][15:0]

  237. +ip_udp_head[1][31:16] + ip_udp_head[1][15:0]

  238. +ip_udp_head[2][31:16] + ip_udp_head[2][15:0]

  239. +ip_udp_head[3][31:16] + ip_udp_head[3][15:0]

  240. +ip_udp_head[4][31:16] + ip_udp_head[4][15:0];

  241. else if(cnt == 'd1)

  242. check_sum <= check_sum[31:16] + check_sum[15:0];

  243. else if(cnt == 'd2)

  244. check_sum <= check_sum[31:16] + check_sum[15:0];

  245. else begin

  246. check_sum <= check_sum;

  247. cnt <= 'd0;

  248. sw_en <= 1'b1;

  249. end

  250. end

  251. PACKET_HEAD:begin

  252. cnt <= cnt + 1'd1;

  253. r_gmii_txd <= packet_head[cnt];

  254. r_gmii_tx_en <= 1'b1;

  255. if(cnt == 'd7) begin

  256. sw_en <= 1'b1;

  257. cnt   <= 'd0;

  258. end

  259. end

  260. ETH_HEAD:begin

  261. cnt <= cnt + 1'd1;

  262. r_crc_en<= 1'b1;

  263. r_gmii_txd <= eth_head[cnt];

  264. if(cnt == 'd13)begin

  265. sw_en <= 1'b1;

  266. cnt <= 'd0;

  267. end

  268. end

  269. IP_UDP_HEAD:begin

  270. cnt <= cnt + 'd1;

  271. case(cnt)

  272. 'd0:r_gmii_txd <= ip_udp_head[frame_cnt][31:24];

  273. 'd1:r_gmii_txd <= ip_udp_head[frame_cnt][23:16];

  274. 'd2:begin

  275. r_gmii_txd <= ip_udp_head[frame_cnt][15:8];

  276. if(frame_cnt == 'd6)

  277. r_tx_req <= 1'b1; 

  278. end

  279. 'd3:begin

  280. frame_cnt <= frame_cnt + 'd1;

  281. r_gmii_txd <= ip_udp_head[frame_cnt][7:0];

  282. cnt <= 'd0;

  283. if(frame_cnt == 'd6)begin

  284. sw_en <= 1'b1;

  285. frame_cnt <= 'd0;

  286. end 

  287. end

  288. default:;

  289. endcase

  290. end

  291. SEND_DATA:begin

  292. cnt_send_state <= cnt_send_state + 'd1;

  293. case(cnt_send_state)

  294. 'd0:r_gmii_txd <= tx_data[31:24];

  295. 'd1:r_gmii_txd <= tx_data[23:16];

  296. 'd2:begin

  297. r_gmii_txd <= tx_data[15:8];

  298. if(data_cnt != data_len -'d1)

  299. r_tx_req <= 1'b1;

  300. end

  301. 'd3:r_gmii_txd <= tx_data[7:0];

  302. default:;

  303. endcase

  304. if(data_cnt <data_len - 'd1)

  305. data_cnt <= data_cnt + 'd1;

  306. else 

  307. data_cnt <= data_cnt;

  308. if((data_cnt + cnt_add < send_data_len - 'd1)&&(data_cnt == data_len -'d1)) //最少发18个数据

  309. cnt_add <= cnt_add + 'd1;

  310. else 

  311. cnt_add <= cnt_add;

  312. if(data_cnt+cnt_add == send_data_len - 'd1)

  313. sw_en <= 1'b1;

  314. end

  315. CRC:begin

  316. r_crc_en <= 1'b0;

  317. cnt <= cnt + 'd1;

  318. case(cnt)

  319. 0:r_gmii_txd <={~crc_next[0],~crc_next[1],~crc_next[2],~crc_next[3],~crc_next[4], ~crc_next[5], ~crc_next[6],~crc_next[7]};

  320. 1:r_gmii_txd <={~crc_data[16], ~crc_data[17], ~crc_data[18],~crc_data[19],~crc_data[20], ~crc_data[21], ~crc_data[22],~crc_data[23]};

  321. 2:r_gmii_txd <={~crc_data[8], ~crc_data[9], ~crc_data[10],~crc_data[11],~crc_data[12], ~crc_data[13], ~crc_data[14],~crc_data[15]};

  322. 3:r_gmii_txd <={~crc_data[0], ~crc_data[1], ~crc_data[2],~crc_data[3],~crc_data[4], ~crc_data[5], ~crc_data[6],~crc_data[7]};

  323. default:;

  324. endcase

  325. if(cnt == 'd3)begin

  326. cnt <= 'd0;

  327. sw_en <=1'b1;

  328. r_tx_done <= 1'b1; 

  329. end

  330. end

  331. endcase

  332. end

  333. end

  334. always@(posedge clk or negedge rst_n) begin

  335. if(!rst_n)

  336. r_crc_clr <= 'd0;

  337. else 

  338. r_crc_clr <= r_tx_done;

  339. end

  340. endmodule

1.5.仿真波形图



二.UDP接受模块

2.1 模块框图

#输入接口

gmii_clk:接受时钟。

sys_rst_n:系统复位。

gmii_rxdv:接受使能。

gmii_rx_data:接受数据,单位是字节。

#输出接口

rec_data_en:数据有效使能。

rec_data:接受的数据。

rec_end:接受完成标志。

rec_data_num:接受数据的个数。

2.2.波形图


2.3逻辑实现思路

首先将输入的rxdv和rx_data数据同步在时钟沿下,在开始状态时,当gmii_rxdv_reg和gmii_rx_data分别为高电平和8‘h55时,进入PACK_HEAD状态,当cnt计数到6且此时的数据是8’hd5时,进入下一个状态ETH_HEAD状态,在这个状态下,要接受目的mac地址,源mac地址,和eth类型,将目的mac地址寄存在des_mac变量中,当des_mac和输入的目的地址相同时,拉高mac_flag信号,同时当cnt等于13的条件下,进入IP_HEAD状态,在此状态下,将目的ip号寄存在des-ip变量下,当des_ip和输入的目的ip相同时,拉高ip_flag信号,同时当cnt等于19的时候,进入UDP_HEAD状态,在此状态下,当cnt计数到7的时候,进入REC_DATA状态,当data_cnt等于dala_len时,进入REC_END状态,当gmii_rxdv_reg为低时,进入IDLE状态。以此循环。

2.4 接受状态机代码

2.5 仿真波形

仿真整体波形

仿真局部波形

三.rgmii接口和gmii接口

3.1rgmii_to_gmii

模块框图

波形图

注:双沿采样变成单沿采样,同步到clk时钟的上升沿。

仿真波形:

3.2gmii_to_rgmii

模块框图

波形图

注:单沿采样同步到双沿采样。

仿真波形:

总结:千兆以太网就到这里了,以后有机会更新ddraxi接口设计或者万兆以太网。

(全文完)

图片

声明:我们尊重原创,也注重分享;文字、图片版权归原作者所有。转载目的在于分享更多信息,不代表本号立场,如有侵犯您的权益请及时联系,我们将第一时间删除,谢谢!

原文链接:

https://blog.youkuaiyun.com/qq_52245062/article/details/140014969

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值