[米联客-安路飞龙DR1-FPSOC] UDP通信篇连载-04 IP层程序设计
软件版本:Anlogic -TD5.9.1-DR1_ES1.1
操作系统:WIN10 64bit
硬件平台:适用安路(Anlogic)FPGA
实验平台:米联客-MLK-L1-CZ06-DR1M90G开发板
板卡获取平台:https://milianke.tmall.com/
登录"米联客"FPGA社区 http://www.uisrc.com 视频课程、答疑解惑!
3.3 IP层
ICMP层数据和UDP层数据都要经过IP层打包或者解包,IP层主要功能为判断数据报文类型,进行IP首部校验,添加包头或者过滤包头,处理ICMP请求。
3.3.1 IP接收模块
该模块的主要功能是接收uiip_arp_rx传入的数据包,通过首部校验判断包头的正确性,对包头进行过滤,并且提取出UDP报文和ICMP报文。
该模块的状态机转换图如图所示。
WAIT_IP_PACKET:等待接收IP包,若I_ip_rvalid拉高,代表数据传入,进入RECORD_IP_HEADER状态。
RECORD_IP_HEADER:接收包头信息,20字节的包头数据全部接收完时,判断收到的IP地址和本地IP地址是否匹配,若不匹配,丢弃该帧,进入WAIT_PACKET_END状态;若匹配,则判断数据包类型。如果收到的数据报文为UDP数据报文,进入OUTPUT_UDP_PACKET状态。如果收到的数据报文为ICMP报文,则将数据传入ICMP子层模块,等待一包数据传输完成后,进入WAIT_PACKET_END状态。
OUTPUT_UDP_PACKET:将有效数据打拍后传输至UDP层,一包数据传输完成后,回到WAIT_IP_PACKET状态。
WAIT_PACKET_END:等待数据有效信号拉低,一帧数据传输完成,回到WAIT_IP_PACKET状态。
1 always@(posedge I_ip_clk or posedge I_reset) begin 2 if(I_reset) begin 3 cnt <= 5'd0; 4 ip_version <= 4'd0; //IP首部-版本:4位数据表示IP版本号,为4时表示IPv4,为6时表示IPv6,IPv4使用较多。 5 ip_header_len <= 4'd0; //IP首部-首部长度:4位数据表示IP首部一共有多少个32位(4个字节)数据。没有可选字段的IP首部长度为20个字节,故首部长度为5 6 ip_tos <= 8'd0; //IP首部-服务类型:8位服务类型被划分成两个子字段:3位优先级字段和4位TOS字段,最后一位固定为0。服务类型为0时表示一般服务。 7 ip_pkg_len <= 16'd0; //IP首部-总长度:16位IP数据报总长度包括IP首部和IP数据部分,以字节为单位。利用IP首部长度和IP数据报总长度可以计算出IP数据报中数据内容的起始位置和长度 8 ip_packet_id <= 16'd0; //IP首部-ID:16位标识字段,用来标识主机发送的每一份数据报。每发送一份报文它的值就会加1 9 ip_packet_flag <= 3'd0; //IP首部-标志字段:3位标志字段的第1位是保留位,第2位表示禁止分片(1表示不分片,0允许分片),第3位标识更多分片,通常为010不分片 10 ip_fragment_offset <= 13'd0; //IP首部-片偏移:13位片偏移,在接收方进行数据报重组时用来标识分片的顺序。 11 ip_packet_ttl <= 8'd0; //IP首部-生存时间: 8位生存时间防止丢失的数据包在无休止的传播,一般被设置为64或者128 12 ip_packet_protocol <= 8'd0; //IP首部-协议:8位协议类型表示此数据报所携带上层数据使用的协议类型,ICMP为1,TCP为6,UDP为17 13 ip_header_checksum <= 16'd0; //IP首部-首部校验和:16位首部校验和,该字段只校验数据报的首部,不包含数据部分 14 ip_src_address <= 32'd0; //IP首部-源IP地址:32位发送端的IP地址 15 ip_dst_address <= 32'd0; //IP首部-目的IP地址:32位接收端的IP地址 16 icmp_pkg_valid <= 1'b0; //icmp数据报文有效信号 17 O_udp_ip_rvalid <= 1'b0; //UDP数据包有效 18 O_udp_ip_rdata <= 8'd0; //UDP数据包有效输出 19 STATE <= WAIT_IP_PACKET; 20 end 21 else begin 22 case(STATE) 23 WAIT_IP_PACKET:begin 24 if(I_ip_rvalid) begin 25 ip_version <= I_ip_rdata[7:4]; //IP首部-版本:4位数据表示IP版本号 26 ip_header_len <= I_ip_rdata[3:0]; //IP首部-首部长度:4位数据表示IP首部一共有多少个32位(4个字节)数据 27 STATE <= RECORD_IP_HEADER; //下一状态,继续接收IP头部信息 28 end 29 else 30 STATE <= WAIT_IP_PACKET; 31 end 32 RECORD_IP_HEADER:begin 33 case(cnt) 34 0: begin ip_tos <= I_ip_rdata; cnt <= cnt + 1'b1;end //IP首部-服务类型:8位服务类型被划分成两个子字段:3位优先级字段和4位TOS字段,最后一位固定为0。服务类型为0时表示一般服务。 35 1: begin ip_pkg_len[15:8] <= I_ip_rdata; cnt <= cnt + 1'b1;end //IP首部-总长度:16位IP数据报总长度包括IP首部和IP数据部分,以字节为单位。 36 2: begin ip_pkg_len[7:0] <= I_ip_rdata; cnt <= cnt + 1'b1;end //IP首部-总长度:16位IP数据报总长度包括IP首部和IP数据部分,以字节为单位。 37 3: begin ip_packet_id[15:8] <= I_ip_rdata; cnt <= cnt + 1'b1;end //IP首部-ID:16位标识字段,用来标识主机发送的每一份数据报。 38 4: begin ip_packet_id[7:0] <= I_ip_rdata; cnt <= cnt + 1'b1;end //IP首部-ID:16位标识字段,用来标识主机发送的每一份数据报。 39 5: begin 40 ip_packet_flag <= I_ip_rdata[7:5]; //IP首部-标志字段:3位标志字段的第1位是保留位,第2位表示禁止分片(1表示不分片,0允许分片),第3位标识更多分片,通常为010不分片 41 ip_fragment_offset[12:8] <= I_ip_rdata[4:0]; //IP首部-片偏移:13位片偏移,在接收方进行数据报重组时用来标识分片的顺序。 42 cnt <= cnt + 1'b1; 43 end 44 6: begin ip_fragment_offset[7:0] <= I_ip_rdata; cnt <= cnt + 1'b1;end //IP首部-片偏移:13位片偏移,在接收方进行数据报重组时用来标识分片的顺序。 45 7: begin ip_packet_ttl[7:0] <= I_ip_rdata; cnt <= cnt + 1'b1;end //IP首部-生存时间:8位生存时间防止丢失的数据包在无休止的传播,一般被设置为64或者128 46 8: begin ip_packet_protocol[7:0] <= I_ip_rdata; cnt <= cnt + 1'b1;end //IP首部-协议:8位协议类型表示此数据报所携带上层数据使用的协议类型,ICMP为1,TCP为6,UDP为17 47 9: begin ip_header_checksum[15:8] <= I_ip_rdata; cnt <= cnt + 1'b1;end //IP首部-首部校验和:16位首部校验和,该字段只校验数据报的首部,不包含数据部分 48 10: begin ip_header_checksum[7:0] <= I_ip_rdata; cnt <= cnt + 1'b1;end //IP首部-首部校验和:16位首部校验和,该字段只校验数据报的首部,不包含数据部分 49 11: begin ip_src_address[31:24] <= I_ip_rdata; cnt <= cnt + 1'b1;end //IP首部-源IP地址:32位发送端的IP地址 50 12: begin ip_src_address[23:16] <= I_ip_rdata; cnt <= cnt + 1'b1;end //IP首部-源IP地址:32位发送端的IP地址 51 13: begin ip_src_address[15:8] <= I_ip_rdata; cnt <= cnt + 1'b1;end //IP首部-源IP地址:32位发送端的IP地址 52 14: begin ip_src_address[7:0] <= I_ip_rdata; cnt <= cnt + 1'b1;end //IP首部-源IP地址:32位发送端的IP地址 53 15: begin ip_dst_address[31:24] <= I_ip_rdata; cnt <= cnt + 1'b1;end //IP首部-目的IP地址:32位接收端的IP地址 54 16: begin ip_dst_address[23:16] <= I_ip_rdata; cnt <= cnt + 1'b1;end //IP首部-目的IP地址:32位接收端的IP地址 55 17: begin ip_dst_address[15:8] <= I_ip_rdata; cnt <= cnt + 1'b1;end //IP首部-目的IP地址:32位接收端的IP地址 56 18: begin 57 ip_dst_address[7:0] <= I_ip_rdata; //IP首部-目的IP地址:32位接收端的IP地址 58 cnt <= 5'd0; 59 if({ip_dst_address[31:8], I_ip_rdata} == I_ip_local_addr) begin //如果收到的IP地址和本地IP地址匹配 60 if(ip_packet_protocol == ICMP_TYPE) begin //如果是ICMP类型 61 icmp_pkg_valid <= 1'b1; //ICMP包有效 62 STATE <= WAIT_PACKET_END; //等待传输结束状态 63 end 64 else if(ip_packet_protocol == UDP_TYPE) begin //如果是UDP包有效 65 icmp_pkg_valid <= 1'b0; 66 STATE <= OUTPUT_UDP_PACKET; //输出UDP包到UDP协议层 67 end 68 else begin 69 icmp_pkg_valid <= 1'b0; 70 STATE <= WAIT_PACKET_END; //等待传输结束状态 71 end 72 end 73 else begin 74 icmp_pkg_valid <= 1'b0; 75 STATE <= WAIT_PACKET_END; //等待传输结束状态 76 end 77 end 78 endcase 79 end 80 OUTPUT_UDP_PACKET:begin 81 if(I_ip_rvalid) begin //打拍后支持输出给UDP协议层 82 O_udp_ip_rvalid <= 1'b1; 83 O_udp_ip_rdata <= I_ip_rdata; 84 STATE <= OUTPUT_UDP_PACKET; 85 end 86 else begin 87 O_udp_ip_rvalid <= 1'b0; 88 O_udp_ip_rdata <= 8'd0; 89 STATE <= WAIT_IP_PACKET; 90 end 91 end 92 WAIT_PACKET_END:begin //等待包传输结束 93 if(I_ip_rvalid) 94 STATE <= WAIT_PACKET_END; 95 else begin 96 icmp_pkg_valid <= 1'b0; 97 STATE <= WAIT_IP_PACKET; 98 end 99 end 100 endcase 101 end 102 end
3.3.2 IP发送模块
该模块的主要功能有:接收uiudp_tx发送的UDP报文和uiip_rx发送的ICMP报文信息,将其封装成IP包并发送至下层uiip_arp_tx模块。
该模块的状态机转换图如图所示。
IDLE:uiip_tx模块处于发送空闲时,接收ICMP和UDP报文发送请求,优先响应ICMP请求。当收到请求时,向uiip_arp_tx模块发送请求信号,通过udp_pkg_en信号标记报文类型,进入WAIT_ACK状态,等待握手响应信号。
WAIT_ACK:接收到I_ip_tbusy信号,代表和IP_ARP层模块握手成功,可以发送数据,进入SEND_IP_HEADER状态。若要发送的是UDP报文,则会发送O_ip_udp_tbusy信号和UDP层进行握手。
SEND_IP_HEADER:在有效数据报文前添加对应的IP包头,发送包头完毕后,若报文类型为UDP报文,进入SEND_UDP_PACKET状态;若为ICMP报文,则进入SEND_ICMP_PACKET状态。
SEND_UDP_PACKET和SEND_ICMP_PACKET:发送完报文中的有效数据后,将标识加1,回到IDLE状态。
1 case(STATE) 2 IDLE:begin 3 if(icmp_pkg_req & (~I_ip_tbusy)) begin //如果有ICMP包需要发送,并且ip_arp_tx模块处于空闲(I_ip_tbusy==0代表tbuf空闲,不在发送数据) 4 O_ip_treq <= 1'b1; //通知ip_arp_tx模块,有IP包需要发送(ICMP包也是IP包) 5 O_ip_udp_tbusy <= 1'b0; //通知udp_layer模块,目前不能发送UDP数据包 6 O_ip_taddr <= icmp_pkg_ip_addr; //保存ip_rx模块接收到的icmp请求的IP地址(因为发送icmp包需要通过IP地址获取远程主机的MAC地址) 7 udp_pkg_en <= 1'b0; //标记是否是udp包,但udp_pkg_en=1 代表ip层发送的数据为UDP包 8 STATE <= WAIT_ACK; 9 end 10 else if(I_ip_udp_treq & (~I_ip_tbusy)) begin //如果有UDP包需要发送 11 O_ip_treq <= 1'b1; //输出ip包发送请求,给ip_arp_tx模块 12 O_ip_udp_tbusy <= 1'b0; //通知udp_layer模块,目前不能发送UDP数据包 13 O_ip_taddr <= I_ip_dest_addr; //udp层提供需要发送的目的主机的IP地址 14 udp_pkg_en <= 1'b1; //标记是否是udp包,但udp_pkg_en=1 代表ip层发送的数据为UDP包 15 STATE <= WAIT_ACK; 16 end 17 else begin 18 O_ip_udp_tbusy <= 1'b0; 19 O_ip_treq <= 1'b0; 20 udp_pkg_en <= 1'b0; 21 STATE <= IDLE; 22 end 23 end 24 WAIT_ACK:begin 25 if(I_ip_tbusy) begin//当发送O_ip_treq后,如果ip_arp_tx模块返回I_ip_tbusy=1 代表ip_layer可以发送IP包(UDP包和ICMP包)到ip_arp_tx模块 26 O_ip_treq <= 1'b0; 27 O_ip_udp_tbusy <= udp_pkg_en ? 1 : 0; //如果udp_pkg_en有效代表发的是UDP包 28 STATE <= SEND_IP_HEADER; //发送IP帧头 29 end 30 else begin 31 O_ip_treq <= 1'b1; 32 O_ip_udp_tbusy <= 1'b0; 33 STATE <= WAIT_ACK; 34 end 35 end 36 SEND_IP_HEADER:begin//发送IP包帧头 37 case(cnt) 38 0 :begin 39 if(I_ip_udp_tvalid | (~udp_pkg_en)) begin //如果是UDP报文包需要发送或者udp_pkg_en==0 是ICMP报文包 40 O_ip_tdata <= {VERSION, IHL}; //版本|首部长度(IP首部一共有多少个32bit数据) 41 O_ip_tvalid <= 1'b1; //通知tbuf IP数据有效 42 packet_id <= ID_BASE + datagram_cnt; //标识,每发送1包该值加1 43 TTL <= 8'h80; //生存时间 44 if(!udp_pkg_en) begin //如果是ICMP包 45 ip_tdata_length <= icmp_pkg_data_len + (IHL << 2); //IP包总长度(IP数据长度+IP首部长度) 46 PROTOCOL <= 8'h01; //IP包类型为 ICMP包 47 end 48 else begin 49 ip_tdata_length <= I_ip_udp_tdata_len + (IHL << 2); 50 PROTOCOL <= 8'h11; //IP包类型为 UDP包 51 end 52 cnt <= cnt + 1'b1; 53 end 54 else 55 cnt <= 5'd0; 56 end 57 1 :begin O_ip_tdata <= TOS; cnt <= cnt + 1'b1;end//服务类型 58 2 :begin O_ip_tdata <= ip_tdata_length[15:8]; cnt <= cnt + 1'b1;end//IP包总长度 59 3 :begin O_ip_tdata <= ip_tdata_length[7:0]; cnt <= cnt + 1'b1;end//IP包总长度 60 4 :begin O_ip_tdata <= packet_id[15:8]; cnt <= cnt + 1'b1;end//IP包标识符,每发送一份报文,其值加1 61 5 :begin O_ip_tdata <= packet_id[7:0]; cnt <= cnt + 1'b1;end//IP包标识符,每发送一份报文,其值加1 62 6 :begin O_ip_tdata <= {FLAG, FRAGMENT_OFFSET[12:8]}; cnt <= cnt + 1'b1;end//标志字段3bit|片偏移共13bit 63 7 :begin O_ip_tdata <= FRAGMENT_OFFSET[7:0]; cnt <= cnt + 1'b1;end//片偏移共13bit 64 8 :begin O_ip_tdata <= TTL; cnt <= cnt + 1'b1;end//生存时间 65 9 :begin O_ip_tdata <= PROTOCOL; cnt <= cnt + 1'b1;end//协议 66 10 :begin O_ip_tdata <= checksum[15:8]; cnt <= cnt + 1'b1;end//校验和 67 11 :begin O_ip_tdata <= checksum[7:0]; cnt <= cnt + 1'b1;end//校验和 68 12 :begin O_ip_tdata <= I_ip_local_addr[31:24]; cnt <= cnt + 1'b1;end//源IP地址32bit 69 13 :begin O_ip_tdata <= I_ip_local_addr[23:16]; cnt <= cnt + 1'b1;end//源IP地址32bit 70 14 :begin O_ip_tdata <= I_ip_local_addr[15:8]; cnt <= cnt + 1'b1;end//源IP地址32bit 71 15 :begin O_ip_tdata <= I_ip_local_addr[7:0]; cnt <= cnt + 1'b1;end//源IP地址32bit 72 16 :begin //目的IP地址(远端主机IP地址) 73 if(!udp_pkg_en) //ICMP报文包 74 O_ip_tdata <= icmp_pkg_ip_addr[31:24]; 75 else //UDP报文包 76 O_ip_tdata <= O_ip_taddr[31:24]; 77 cnt <= cnt + 1'b1; 78 end 79 17 :begin //目的IP地址(远端主机IP地址) 80 if(!udp_pkg_en) //ICMP报文包 81 O_ip_tdata <= icmp_pkg_ip_addr[23:16]; 82 else //UDP报文包 83 O_ip_tdata <= O_ip_taddr[23:16]; 84 cnt <= cnt + 1'b1; 85 end 86 18 :begin //目的IP地址(远端主机IP地址) 87 if(!udp_pkg_en) begin 88 O_ip_tdata <= icmp_pkg_ip_addr[15:8]; 89 icmp_pkg_busy <= 1'b1;//icmp_pkg_tx代码上必须时序上,在SEND_ICMP_PACKET状态输出ICMP报文包 90 end 91 else begin 92 O_ip_tdata <= O_ip_taddr[15:8]; 93 icmp_pkg_busy <= 1'b0; 94 end 95 cnt <= cnt + 1'b1; 96 end 97 19 :begin //目的IP地址(远端主机IP地址) 98 cnt <= 5'd0; 99 if(!udp_pkg_en) begin 100 O_ip_tdata <= icmp_pkg_ip_addr[7:0]; 101 STATE <= SEND_ICMP_PACKET; 102 end 103 else begin 104 O_ip_tdata <= O_ip_taddr[7:0]; 105 STATE <= SEND_UDP_PACKET; 106 end 107 end 108 default: cnt <= 5'd0; 109 endcase 110 end 111 SEND_UDP_PACKET:begin //发送UDP报文包 112 if(trans_data_cnt == (ip_tdata_length - 16'd20)) begin //20个字节为IP首部,这里相等代表数据发送结束 113 O_ip_udp_tbusy <= 1'b0; 114 O_ip_tvalid <= 1'b0; 115 O_ip_tdata <= 8'd0; 116 datagram_cnt <= datagram_cnt + 16'h0001; //没发送完1帧报文,该值加1 117 trans_data_cnt <= 16'd0; 118 STATE <= IDLE; 119 end 120 else begin //发送有效的UDP报文数据部分 121 O_ip_tvalid <= 1'b1; 122 O_ip_tdata <= shift_data_out; //从shift移位寄存器移出数据 123 trans_data_cnt <= trans_data_cnt + 1'b1; //UDP报文包有效数据部分计数器 124 STATE <= SEND_UDP_PACKET; 125 end 126 end 127 SEND_ICMP_PACKET:begin //发送ICMP报文包 128 if(icmp_pkg_valid) begin 129 O_ip_tvalid <= 1'b1; 130 O_ip_tdata <= icmp_pkg_data; 131 STATE <= SEND_ICMP_PACKET; 132 end 133 else begin 134 O_ip_tvalid <= 1'b0; 135 O_ip_tdata <= 8'd0; 136 icmp_pkg_busy <= 1'b0; 137 datagram_cnt <= datagram_cnt + 16'h0001;//每发送完1帧报文,该值加1 138 STATE <= IDLE; 139 end 140 end 141 endcase
3.3.3 IP首部校验模块
正常计算IP首部校验和时,将校验位置0。由于每两字节数据相加得到的不再产生进位结果,为最终校验和的取反,所以当计算过程中加入校验位时,相加得到的结果应当为全1。若结果不为全1,则数据校验错误,将O_check_rerror信号置为高。
1 `timescale 1ns / 1ps 2 3 module ip_header_checksum( 4 input wire I_clk, 5 input wire I_reset, 6 input wire I_ip_rdata_valid, 7 input wire [7:0] I_ip_rdata, 8 output wire O_checksum_rerror 9 ); 10 reg checksum_correct; 11 assign O_checksum_rerror = ~checksum_correct; 12 13 reg [1:0] state; 14 reg [3:0] cnt; 15 wire [16:0] tmp_accum1; 16 reg [15:0] accum1, accum2; 17 18 assign tmp_accum1 = accum1 + accum2; 19 20 always @(posedge I_clk or posedge I_reset) begin 21 if(I_reset) begin 22 state <= 2'd0; 23 cnt <= 4'd0; 24 accum1 <= 16'd0; 25 accum2 <= 16'd0; 26 checksum_correct <= 1'b1; 27 end 28 else begin 29 case(state) 30 0: begin 31 if(I_ip_rdata_valid) begin 32 accum1[15:8] <= I_ip_rdata; 33 state <= 2'd1; 34 end 35 else begin 36 accum1[15:8] <= 8'd0; 37 state <= 2'd0; 38 end 39 end 40 1: begin accum1[7:0] <= I_ip_rdata; state <= 2'd2; end 41 2: begin 42 if(cnt == 4'd9) begin 43 if((tmp_accum1[15:0] + tmp_accum1[16]) != 16'hffff) 44 checksum_correct <= 1'b0; 45 cnt <= 4'd0; 46 state <= 2'd3; 47 end 48 else begin 49 accum2 <= tmp_accum1[15:0] + tmp_accum1[16]; 50 accum1[15:8] <= I_ip_rdata; 51 cnt <= cnt + 1'b1; 52 state <= 2'd1; 53 end 54 end 55 3: begin 56 accum1 <= 16'd0; 57 accum2 <= 16'd0; 58 if(I_ip_rdata_valid) 59 state <= state; 60 else 61 state <= 2'd0; 62 end 63 endcase 64 end 65 end 66 endmodule
本文来米联客(milianke),作者:米联客(milianke),转载请注明原文链接:https://www.cnblogs.com/milianke/p/18351342