本文主要是介绍60.以太网数据回环实验(3)以太网数据收发器接收模块,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
(1)状态设计:
- ST_IDLE :空闲状态
- ST_PREAMBLE :前导码+帧起始界定符
- ST_ETH_HEAD :以太网帧头:目标MAC地址+源MAC地址+类型/长度
- ST_IP_HEAD :IP首部
- ST_UDP_HEAD :UDP首部(8个字节)
- ST_RX_DATA :接收数据状态
- ST_RX_DONE :接收完成状态
(2)状态转移图:
(3)以太网帧头:目标MAC地址(6个字节)+源MAC地址(6个字节)+ 类型/长度(2个字节)(08 00 表示基于IP协议) 目标MAC地址需要使用一个寄存器寄存起来。
(4)IP首部:目的 IP 地址在首部的第 17 至 20字节,需要使用一个寄存器寄存起来。
比如第一个字节为45,则IP地址长度对应5左移两位(010100),即长度为20
(5)UDP首部:第五个字节和第六个字节包含了UDP长度信息,例如:0028(40),有效数据长度等于UDP长度-首部长度(8个字节)
(6)UDP接收模块代码:
//UDP接收模块设计module udp_rx(input wire gmii_rxc ,input wire reset_n ,input wire gmii_rx_dv , //GMII输入数据有效信号input wire [7:0] gmii_rxd , //GMII输入数据output reg [31:0] rec_data , //以太网接收后转化为32位的数据output reg rec_en , //以太网接收的数据使能信号output reg rec_pkt_done , //以太网单包数据接收完成信号 output reg [15:0] rec_byte_num //以太网接收的有效数据字节数);/*--------------参数定义,parameter全局变量,可以在上层进行修改,而localparam则是局部变量,仅在该模块使用-----------*/
//开发板MAC地址(48位)
parameter BOARO_MAC = 48'hff_ff_ff_ff_ff_ff ;//开发板IP地址(32位)
parameter BOARO_IP = {8'd0,8'd0,8'd0,8'd0} ;//状态机状态定义 独热吗编码
localparam ST_IDLE = 7'b000_0001 ; //空闲状态,等待接收前导码
localparam ST_PREAMBLE = 7'b000_0010 ; //接收前导码和帧起始界定符状态
localparam ST_ETH_HEAD = 7'b000_0100 ; //接收以太网帧头状态
localparam ST_IP_HEAD = 7'b000_1000 ; //接收IP首部状态
localparam ST_UDP_HEAD = 7'b001_0000 ; //接收UDP首部
localparam ST_RX_DATA = 7'b010_0000 ; //接收有效数据状态
localparam ST_RX_DONE = 7'b100_0000 ; //接收完成状态//以太网协议类型定义 IP协议
localparam ETH_TYPE = 16'h0800 ;/*--------------寄存器变量定义-----*/
reg [6:0] cur_state ; //当前状态
reg [6:0] next_state ; //下一个状态reg sw_en ; //下一个状态跳转标志信号
reg error_en ; //接收错误标志信号
reg [4:0] cnt_byte ; //字节计数器
reg [47:0] des_mac ; //目标MAC地址,在本模块中及开发板MAC地址
reg [15:0] eth_type ; //以太网类型
reg [31:0] des_ip ; //目标IP地址,在本模块中及开发板IP地址
reg [5:0] ip_len ; //IP首部长度
reg [15:0] udp_len ; //UDP长度
reg [15:0] cnt_data ; //数据长度计算
reg [15:0] data_len ; //UDP有效数据长度
reg [1:0] cnt_rec_data ; //8位转32位计数器/*--------------三段式状态机编写主程序--------*/
//第一段:时序逻辑描述状态转移
always@(posedge gmii_rxc or negedge reset_n)beginif(!reset_n)cur_state <= ST_IDLE ;else cur_state <= next_state ;
end//第二段:组合逻辑判断状态机转移条件
always@(*)beginnext_state = ST_IDLE ;case(cur_state)ST_IDLE :if(sw_en)next_state = ST_PREAMBLE;else next_state = ST_IDLE;ST_PREAMBLE :if(sw_en)next_state = ST_ETH_HEAD;else if(error_en)next_state = ST_RX_DONE;else next_state = ST_PREAMBLE;ST_ETH_HEAD :if(sw_en)next_state = ST_IP_HEAD;else if(error_en)next_state = ST_RX_DONE;else next_state = ST_ETH_HEAD;ST_IP_HEAD :if(sw_en)next_state = ST_UDP_HEAD;else if(error_en)next_state = ST_RX_DONE;else next_state = ST_IP_HEAD;ST_UDP_HEAD :if(sw_en)next_state = ST_RX_DATA;else next_state = ST_UDP_HEAD;ST_RX_DATA :if(sw_en)next_state = ST_RX_DONE;else next_state = ST_RX_DATA;ST_RX_DONE :if(sw_en)next_state = ST_IDLE;else next_state = ST_RX_DONE;default:next_state = ST_IDLE ;endcase
end//第三段:时序逻辑描述寄存器变量和状态输出
always@(posedge gmii_rxc or negedge reset_n)beginif(!reset_n)beginsw_en <= 1'd0; error_en <= 1'd0; cnt_byte <= 5'd0; des_mac <= 48'd0; eth_type <= 16'd0; des_ip <= 32'd0;ip_len <= 6'd0;udp_len <= 16'd0; cnt_data <= 16'd0; data_len <= 16'd0; cnt_rec_data <= 2'd0; rec_data <= 32'd0;rec_en <= 1'd0;rec_pkt_done <= 1'd0;rec_byte_num <= 16'd0; endelse beginsw_en <= 1'd0; error_en <= 1'd0;rec_en <= 1'd0;rec_pkt_done <= 1'd0;case(next_state)ST_IDLE :begin //GMII输入有效信号拉高,接收到0x55,跳转至下一个状态if((gmii_rx_dv == 1'd1) && (gmii_rxd == 8'h55))sw_en <= 1'd1;end ST_PREAMBLE :beginif(gmii_rx_dv == 1'd1)begincnt_byte <= cnt_byte + 5'd1; if((cnt_byte < 5'd6) && (gmii_rxd != 8'h55)) //如果正确,0,1,2,3,4,5 再加上空闲跳转到该状态的一个0x55,总共7个0x55 error_en <= 1'd1;else if(cnt_byte == 5'd6)begincnt_byte <= 5'd0;if(gmii_rxd == 8'hd5) //一个0xd5sw_en <= 1'd1;else error_en <= 1'd1;endendend ST_ETH_HEAD :beginif(gmii_rx_dv == 1'd1)begincnt_byte <= cnt_byte + 5'd1;if(cnt_byte < 5'd6) //0,1,2,3,4,5 6个8位数据,并凑出一个48位的MAC地址des_mac <= {des_mac[39:0],gmii_rxd};else if(cnt_byte == 5'd12) //6,7,8,9,10,11 6个8位数据,是源MAC地址,12,13位为协议类型eth_type[15:8] <= gmii_rxd;else if(cnt_byte == 5'd13)begineth_type[7:0] <= gmii_rxd;cnt_byte <= 5'd0;if(((des_mac == BOARO_MAC) || (des_mac == 48'hff_ff_ff_ff_ff_ff))&& eth_type[15:8] == ETH_TYPE[15:8]&& gmii_rxd == ETH_TYPE[7:0])sw_en <= 1'd1;else error_en <= 1'd1;endendendST_IP_HEAD :beginif(gmii_rx_dv == 1'd1)begincnt_byte <= cnt_byte + 5'd1; if(cnt_byte == 5'd0) //IP首部第一个字节的后四位左移2位 代表IP首部的长度ip_len <= {gmii_rxd[3:0],2'd0}; else if((cnt_byte >= 5'd16) && (cnt_byte <= 5'd18)) //IP首部的后四个字节组成目标IP地址des_ip <= {des_ip[23:0],gmii_rxd};else if(cnt_byte == 5'd19)begindes_ip <= {des_ip[23:0],gmii_rxd};if((des_ip[23:0] == BOARO_IP[31:8])&&(gmii_rxd == BOARO_IP[7:0]))if(cnt_byte == ip_len - 1'b1)beginsw_en <= 1'd1;cnt_byte <= 5'd0; endelse beginerror_en <= 1'd1;cnt_byte <= 5'd0;endendelse if(cnt_byte == ip_len - 1'b1)beginsw_en <= 1'd1;cnt_byte <= 5'd0; endendend /*------UDP首部(8个字节):第五个字节和第六个字节包含了UDP长度信息,例如:0028(40),有效数据长度等于UDP长度-首部长度(8个字节)----*/ST_UDP_HEAD :beginif(gmii_rx_dv == 1'd1)begincnt_byte <= cnt_byte + 5'd1;if(cnt_byte == 5'd4)udp_len[15:8] <= gmii_rxd;else if(cnt_byte == 5'd5)udp_len[7:0] <= gmii_rxd;else if(cnt_byte == 5'd7)begindata_len <= udp_len - 16'd8;sw_en <= 1'd1;cnt_byte <= 5'd0;end endendST_RX_DATA :beginif(gmii_rx_dv == 1'd1)begincnt_data <= cnt_data + 16'd1;cnt_rec_data <= cnt_rec_data + 2'd1;if(cnt_data == data_len - 16'd1)beginsw_en <= 1'd1;cnt_data <= 16'd0;cnt_rec_data <= 2'd0;rec_pkt_done <= 1'd1;rec_en <= 1'd1;rec_byte_num <= data_len; endif(cnt_rec_data == 2'd0)rec_data[31:24] <= gmii_rxd;else if(cnt_rec_data == 2'd1)rec_data[23:16] <= gmii_rxd;else if(cnt_rec_data == 2'd2)rec_data[15:8] <= gmii_rxd;else if(cnt_rec_data == 2'd3)beginrec_en <= 1'd1;rec_data[7:0] <= gmii_rxd;endendendST_RX_DONE :begin //单包数据接收完成if((gmii_rx_dv == 1'd0) && (sw_en == 1'd0))sw_en <= 1'd1;enddefault: ; endcaseend
endendmodule
(7)仿真代码:
`timescale 1ns / 1psmodule tb_udp_rx;reg gmii_rxc ; reg reset_n ; reg start_flag ;reg gmii_rx_dv ;reg [7:0] cnt ;reg [7:0] data_mem[85:0] ;wire [7:0] gmii_rxd ; wire [31:0] rec_data ; wire rec_en ; wire rec_pkt_done ; wire [15:0] rec_byte_num ;initial gmii_rxc = 1'd1;
always #10 gmii_rxc = ~gmii_rxc;initial beginreset_n <= 1'd0;start_flag <= 1'd0;#200reset_n <= 1'd1;#200start_flag <= 1'd1;#40start_flag <= 1'd0;#3000$stop;
end initial $readmemh("D:/FPGA/35_eth_udp_loop/vivado_prj/project_1.srcs/sim_1/new/data.txt",data_mem);assign gmii_rxd = (gmii_rx_dv == 1'd1)? data_mem[cnt] : 8'd0;always@(negedge gmii_rxc or negedge reset_n)if(!reset_n)cnt <= 1'd0;else if(gmii_rx_dv == 1'd1)cnt <= cnt + 1'b1;else cnt <= cnt;always@(negedge gmii_rxc or negedge reset_n)if(!reset_n)gmii_rx_dv <= 1'd0;else if(cnt == 8'd85) //8 + 14 + 20 + 8 + 32 + 4 = 86gmii_rx_dv <= 1'd0;else if(start_flag)gmii_rx_dv <= 1'd1;else gmii_rx_dv <= gmii_rx_dv;defparam udp_rx_inst.BOARO_MAC = 48'h12_34_56_78_9a_bc;
defparam udp_rx_inst.BOARO_IP = 32'ha9_fe_01_17 ;udp_rx udp_rx_inst(.gmii_rxc (gmii_rxc ),.reset_n (reset_n ),.gmii_rx_dv (gmii_rx_dv ), .gmii_rxd (gmii_rxd ), .rec_data (rec_data ), .rec_en (rec_en ), .rec_pkt_done (rec_pkt_done ), .rec_byte_num (rec_byte_num ) );endmodule
(8)仿真波形:
这篇关于60.以太网数据回环实验(3)以太网数据收发器接收模块的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!