赞
踩
接口描述:
//输入接口:
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使能和清空信号。
开始状态是空闲状态;当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状态,至此一帧的以太网数据发送完成。
//时序逻辑表示状态寄存 always @(posedge gmii_clk or negedge sys_rst_n) begin if(!sys_rst_n) current_state <= IDLE; else current_state <= next_state; end //组合逻辑表示状态转移 always @(*) begin case(current_state) IDLE:begin if(sw_en) next_state = CHECK_SUM; else next_state = IDLE; end CHECK_SUM:begin if(sw_en) next_state = PACKET_HEAD; else next_state = CHECK_SUM; end PACKET_HEAD:begin if(sw_en) next_state = ETH_HEAD; else next_state = PACKET_HEAD; end ETH_HEAD:begin if(sw_en) next_state = IP_UDP_HEAD; else next_state = ETH_HEAD; end IP_UDP_HEAD:begin if(sw_en) next_state = SEND_DATA; else next_state = IP_UDP_HEAD; end SEND_DATA:begin if(sw_en) next_state = CRC; else next_state = SEND_DATA; end CRC:begin if(sw_en) next_state = IDLE; else next_state = CRC; end default: next_state = IDLE; endcase end //时序逻辑表示状态输出 //send_end always @(posedge gmii_clk or negedge sys_rst_n ) begin if(!sys_rst_n) send_end<='d0; else if(next_state == CRC &&cnt == 'd3) send_end<='d1; else send_end<='d0; end //read_data_req always @(posedge gmii_clk or negedge sys_rst_n ) begin if(!sys_rst_n) read_data_req <= 'd0; else if(next_state == IP_UDP_HEAD &&cnt == 'd2&&frame_cnt =='d6) read_data_req <= 'd1; else if(next_state == SEND_DATA &&cnt_send_state == 'd2&&(data_cnt!=data_len-'d1)) read_data_req <= 'd1; else read_data_req <= 'd0; end //eth_tx_en always @(posedge gmii_clk or negedge sys_rst_n) begin if(!sys_rst_n) eth_tx_en <= 1'b0; else if(next_state != IDLE && next_state != CHECK_SUM) eth_tx_en <= 1'b1; else eth_tx_en <= 1'b0; end //eth_tx_data always @(posedge gmii_clk or negedge sys_rst_n) begin if(!sys_rst_n) eth_tx_data <= 'd0; else if(next_state == PACKET_HEAD) eth_tx_data <= packet_head[cnt]; else if(next_state == ETH_HEAD) eth_tx_data <= eth_head[cnt]; else if(next_state == IP_UDP_HEAD) begin case(cnt) 0:eth_tx_data <= ip_udp_head[frame_cnt][31:24]; 1:eth_tx_data <= ip_udp_head[frame_cnt][23:16]; 2:eth_tx_data <= ip_udp_head[frame_cnt][15:8]; 3:eth_tx_data <= ip_udp_head[frame_cnt][7:0]; default:eth_tx_data <= eth_tx_data; endcase end else if(next_state == SEND_DATA ) begin case(cnt_send_state) 0:eth_tx_data <= send_data[31:24]; 1:eth_tx_data <= send_data[23:16]; 2:eth_tx_data <= send_data[15:8]; 3:eth_tx_data <= send_data[7:0]; default:eth_tx_data <= eth_tx_data; endcase end else if(next_state == CRC) begin case(cnt) 0:eth_tx_data <= {~crc_next[0],~crc_next[1],~crc_next[2],~crc_next[3],~crc_next[4], ~crc_next[5], ~crc_next[6],~crc_next[7]}; 1:eth_tx_data <= {~crc_data[16], ~crc_data[17], ~crc_data[18],~crc_data[19],~crc_data[20], ~crc_data[21], ~crc_data[22],~crc_data[23]}; 2:eth_tx_data <= {~crc_data[8], ~crc_data[9], ~crc_data[10],~crc_data[11],~crc_data[12], ~crc_data[13], ~crc_data[14],~crc_data[15]}; 3:eth_tx_data <= {~crc_data[0], ~crc_data[1], ~crc_data[2],~crc_data[3],~crc_data[4], ~crc_data[5], ~crc_data[6],~crc_data[7]}; default:eth_tx_data <= eth_tx_data; endcase end else eth_tx_data <= eth_tx_data; end //crc_en always @(posedge gmii_clk or negedge sys_rst_n) begin if(!sys_rst_n) crc_en <= 'd0; else if(next_state == ETH_HEAD||next_state == IP_UDP_HEAD || next_state == SEND_DATA) crc_en <= 'd1; else crc_en <= 'd0; end //crc_clr always @(posedge gmii_clk or negedge sys_rst_n) begin if(!sys_rst_n) crc_clr <= 'd0; else crc_clr <= send_end; end
#输入接口
gmii_clk:接受时钟。
sys_rst_n:系统复位。
gmii_rxdv:接受使能。
gmii_rx_data:接受数据,单位是字节。
#输出接口
rec_data_en:数据有效使能。
rec_data:接受的数据。
rec_end:接受完成标志。
rec_data_num:接受数据的个数。
首先将输入的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状态。以此循环。
//时序逻辑表示状态寄存 always @(posedge gmii_clk or negedge sys_rst_n) begin if(!sys_rst_n) current_state <= IDLE; else current_state <= next_state; end //组合逻辑表示状态转移 always @(*) begin next_state = IDLE; case(current_state) IDLE:begin if(sw_en) next_state = PACKET_HEAD; else next_state = IDLE; end PACKET_HEAD:begin if(sw_en) next_state = ETH_HEAD; else next_state = PACKET_HEAD; end ETH_HEAD:begin if(sw_en) next_state = IP_HEAD; else next_state = ETH_HEAD; end IP_HEAD:begin if(sw_en) next_state = UDP_HEAD; else next_state = IP_HEAD; end UDP_HEAD:begin if(sw_en) next_state = REC_DATA; else next_state = UDP_HEAD; end REC_DATA:begin if(sw_en) next_state = REC_END; else next_state = REC_DATA; end REC_END:begin if(sw_en) next_state = IDLE; else next_state = REC_END; end endcase end //时序逻辑表示状态输出 //rec_data_en always @(posedge gmii_clk or negedge sys_rst_n) begin if(!sys_rst_n) rec_data_en <= 'd0; else if(next_state == REC_DATA && cnt_rec_data == 'd3) rec_data_en <= 'd1; else rec_data_en <= 'd0; end //rec_data always @(posedge gmii_clk or negedge sys_rst_n) begin if(!sys_rst_n) rec_data <= 'd0; else if(next_state == REC_DATA && cnt_rec_data <= 'd3) rec_data <= {rec_data[23:0],gmii_rx_data_reg}; end //rec_end always @(posedge gmii_clk or negedge sys_rst_n) begin if(!sys_rst_n) rec_end <= 'd0; else if(next_state == REC_DATA && (cnt_data ==data_len-'d1)) rec_end <= 'd1; else rec_end <= 'd0; end //rec_data_num always @(posedge gmii_clk or negedge sys_rst_n) begin if(!sys_rst_n) rec_data_num <= 'd0; else if(next_state == REC_DATA && (cnt_data ==data_len-'d1)) rec_data_num <= data_len; end
仿真整体波形
仿真局部波形
模块框图
波形图
注:双沿采样边成单沿采样,同步到clk时钟的上升沿。
模块框图
波形图
注:单沿采样同步到双沿采样。
总结:千早以太网就到这里了,以后有机会更新ddr或者万兆以太网。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。