软件版本: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.6 ICMP层
该层在程序中为IP层的子层,设计了接收ICMP请求包并回复的功能。
3.6.1 ICMP接收模块
该模块首先过滤掉ICMP报文头部,并且对头部数据进行校验,校验方式和IP首部校验相同。
//ICMP报文的校验和
always@(posedge I_clk or posedge I_reset) begin
if(I_reset) begin
accum1 <= 16'd0;
accum2 <= 32'd0;
checksum_state <= 2'd0;
checksum_correct <= 1'b1;
end
else begin
case(checksum_state)
0:begin
if(I_icmp_pkg_valid) begin
accum1[15:8] <= I_icmp_pkg_data;
checksum_state <= 2'd1;
end
else begin
accum1[15:8] <= 8'd0;
checksum_state <= 2'd0;
end
end
1:begin
accum1[7:0] <= I_icmp_pkg_data;
checksum_state <= 2'd2;
end
2:begin
if(!I_icmp_pkg_valid) begin
checksum_state <= 2'd3;
if((tmp_accum1[15:0] + tmp_accum1[31:16]) != 16'hffff)
checksum_correct <= 1'b0;
else
checksum_correct <= 1'b1;
end
else begin
accum2 <= tmp_accum1;
accum1[15:8] <= I_icmp_pkg_data;
checksum_state <= 2'd1;
end
end
3:begin
accum1 <= 16'd0;
accum2 <= 32'd0;
checksum_state <= 2'd0;
end
endcase
end
end
将报文的附加数据存入FIFO中,并向icmp_pkg_tx模块发送ping应答信息,请求发送ping应答包。
//以下模块完成ICMP报文包echo ping应答的请求,并且先缓存到ip_layer的FIFO中
always@(posedge I_clk or posedge I_reset) begin
if(I_reset) begin
cnt <= 4'd0;
type1 <= 8'd0;
code <= 8'd0;
echo_data_cnt <= 10'd0;
checksum <= 16'd0;
O_icmp_req_en <= 1'b0;
O_icmp_req_id <= 16'd0;
O_icmp_req_sq_num <= 16'd0;
O_icmp_ping_echo_data_valid <= 1'b0;
O_icmp_ping_echo_data <= 8'd0;
O_icmp_req_checksum <= 16'd0;
STATE <= RECORD_ICMP_HEADER;
end
else begin
case(STATE)
RECORD_ICMP_HEADER:begin
O_icmp_req_en <= 1'b0;
echo_data_cnt <= 10'd0;
if(I_icmp_pkg_valid) //ICMP报文有效
case(cnt)
0: begin type1 <= I_icmp_pkg_data; cnt <= cnt + 1'b1;end //ICMP报文首部-类型:8位数表示错误类型的差错报文或者查询类型的报告报文,一般是查询报文(0代表回显应答(ping应答);1代表查
1: begin code <= I_icmp_pkg_data; cnt <= cnt + 1'b1;end //ICMP报文首部-类型:代码占用8位数据,根据ICMP差错报文的类型,进一步分析错误的原因
2: begin checksum[15:8] <= I_icmp_pkg_data; cnt <= cnt + 1'b1;end //ICMP报文首部-校验和:16位校验和的计算方法与IP首部校验和计算方法一致,该校验和需要对ICMP首部和ICMP数据做校验
3: begin checksum[7:0] <= I_icmp_pkg_data; cnt <= cnt + 1'b1;end //ICMP报文首部-校验和:16位校验和的计算方法与IP首部校验和计算方法一致,该校验和需要对ICMP首部和ICMP数据做校验
4: begin O_icmp_req_id[15:8] <= I_icmp_pkg_data; cnt <= cnt + 1'b1;end //ICMP报文首部-标识符:16位标识符对每一个发送的数据报进行标识
5: begin O_icmp_req_id[7:0] <= I_icmp_pkg_data; cnt <= cnt + 1'b1;end //ICMP报文首部-标识符:16位标识符对每一个发送的数据报进行标识
6: begin O_icmp_req_sq_num[15:8] <= I_icmp_pkg_data; cnt <= cnt + 1'b1;end //ICMP报文首部-序列号:16位对发送的每一个数据报文进行编号
7: begin O_icmp_req_sq_num[7:0] <= I_icmp_pkg_data; cnt <= cnt + 1'b1;end //ICMP报文首部-序列号:16位对发送的每一个数据报文进行编号
8: begin
if(type1 == PING_REQUEST && code == 8'h00) begin //如果是远端主机发的ping请求包,那么本地主机需要返回一个ping应答包
O_icmp_ping_echo_data_valid <= 1'b1; //ping应答有效
O_icmp_ping_echo_data <= I_icmp_pkg_data;
end
else begin
O_icmp_ping_echo_data_valid <= 1'b0;
O_icmp_ping_echo_data <= 8'd0;
end
cnt <= 4'd0;
STATE <= WAIT_PACKET_END;
end
default: STATE <= RECORD_ICMP_HEADER;
endcase
else
STATE <= RECORD_ICMP_HEADER;
end
WAIT_PACKET_END:begin
if(I_icmp_pkg_valid) begin //继续接收ICMP 报文
if(O_icmp_ping_echo_data_valid) //ping应答有效
echo_data_cnt <= echo_data_cnt + 1'b1;//ICMP包计数器
else
echo_data_cnt <= 10'd0;
O_icmp_ping_echo_data_valid <= O_icmp_ping_echo_data_valid;
O_icmp_ping_echo_data <= I_icmp_pkg_data;
STATE <= WAIT_PACKET_END;
end
else begin
if(O_icmp_ping_echo_data_valid) begin
O_icmp_req_en <= 1'b1; //通知ip_tx模块接收到ICMP报文包ping请求,并且发送一个echo ping应答
O_icmp_req_checksum <= checksum_temp; //输出校验和
end
else begin
O_icmp_req_en <= 1'b0;
O_icmp_req_checksum <= 16'd0;
end
echo_data_cnt <= echo_data_cnt;
O_icmp_ping_echo_data_valid <= 1'b0;
O_icmp_ping_echo_data <= 8'd0;
STATE <= RECORD_ICMP_HEADER;
end
end
endcase
end
end
3.6.2 ICMP发送模块
该模块收到icmp_pkg_ctrl模块发送的ping应答包使能信号,寄存接收到的信息包括标识符、序列号、校验和、地址和数据长度,通过计数器添加报文头部,再将附加数据从FIFO中读出,组成ping应答包发送到ip_tx模块。
always@(posedge I_clk or posedge I_reset) begin
if(I_reset) begin
cnt1 <= 4'd0;
cnt2 <= 10'd0;
request_id <= 16'd0;
request_sq_num <= 16'd0;
request_ip_taddress <= 32'd0;
checksum <= 16'd0;
echo_data_length <= 10'd0;
O_icmp_pkg_req <= 1'b0;
O_icmp_pkg_valid <= 1'b0;
O_icmp_pkg_data <= 8'd0;
O_icmp_ping_echo_ren <= 1'b0;
STATE <= WAIT_ICMP_PACKET;
end
else begin
case(STATE)
WAIT_ICMP_PACKET:begin
if(I_icmp_req_en) begin//当接收到ICMP echo ping包,先保存该包的基本信息到寄存器
request_id <= I_icmp_req_id; //ICMP包的标识符
request_sq_num <= I_icmp_req_sq_num; //ICMP包的序列号
request_ip_taddress <= I_icmp_req_ip_addr; //ICMP包的地址
checksum <= I_icmp_req_checksum; //ICMP包的校验和
echo_data_length <= I_icmp_ping_echo_data_len; //ICMP包的长度
O_icmp_pkg_req <= 1'b1; //请求ip_tx模块发送部分,发送ICMP报文
STATE <= WAIT_PACKET_SEND; //发送ICMP包状态
end
else begin
request_id <= 16'd0;
request_sq_num <= 16'd0;
request_ip_taddress <= 32'd0;
checksum <= 16'd0;
echo_data_length <= 10'd0;
O_icmp_pkg_req <= 1'b0;
STATE <= WAIT_ICMP_PACKET;
end
end
WAIT_PACKET_SEND:begin
if(I_icmp_pkg_busy) begin//该信号来自ip_tx模块,当有效代表ip_tx模块已经开始准备发送ICMP包,这里需要对ip_tx代码部分时序逻辑确保数据正确给到ip_tx模块
O_icmp_pkg_req <= 1'b0;
O_icmp_pkg_valid <= 1'b1;
O_icmp_pkg_data <= PING_REPLY_TYPE;//回显应答(ping应答)的类型
STATE <= SEND_PACKET;
end
else begin
O_icmp_pkg_req <= 1'b1;
O_icmp_pkg_valid <= 1'b0;
O_icmp_pkg_data <= 8'd0;
STATE <= WAIT_PACKET_SEND;
end
end
SEND_PACKET:begin
case(cnt1)
0 :begin O_icmp_pkg_data <= 8'h00; cnt1 <= cnt1 + 1'b1;end//回显应答(ping应答)的代码
1 :begin O_icmp_pkg_data <= checksum[15:8]; cnt1 <= cnt1 + 1'b1;end//ICMP报文包校验和,直接获取远程主机发送的Ping包校验和
2 :begin O_icmp_pkg_data <= checksum[7:0]; cnt1 <= cnt1 + 1'b1;end//ICMP报文包校验和,直接获取远程主机发送的Ping包校验和
3 :begin O_icmp_pkg_data <= request_id[15:8]; cnt1 <= cnt1 + 1'b1;end//ICMP报文标识符,直接获取远程主机发送的Ping包标识符
4 :begin O_icmp_pkg_data <= request_id[7:0]; cnt1 <= cnt1 + 1'b1;end//ICMP报文标识符,直接获取远程主机发送的Ping包标识符
5 :begin O_icmp_pkg_data <= request_sq_num[15:8]; cnt1 <= cnt1 + 1'b1;end//ICMP报文编码,直接获取远程主机发送的Ping序列号
6 :begin //从echo FIFO中读取ICMP echo报文的数据部分
O_icmp_pkg_data <= request_sq_num[7:0];
cnt1 <= cnt1 + 1'b1;
O_icmp_ping_echo_ren <= 1'b1;
end
7 :begin//ICMP报文包的数据有效部分
O_icmp_pkg_valid <= 1'b1;
O_icmp_pkg_data <= I_icmp_ping_echo_data;
if(cnt2 == (echo_data_length - 1)) begin
cnt2 <= 10'd0;
O_icmp_ping_echo_ren <= 1'b0;
cnt1 <= cnt1 + 1'b1;
end
else begin
cnt2 <= cnt2 + 1'b1;
O_icmp_ping_echo_ren <= 1'b1;
cnt1 <= cnt1;
end
end
8 :begin
cnt1 <= 4'd0;
O_icmp_pkg_data <= 8'd0;
O_icmp_pkg_valid <= 1'b0;
STATE <= WAIT_ICMP_PACKET;
end
default:;
endcase
end
endcase
end
end
本文来米联客(milianke),作者:米联客(milianke),转载请注明原文链接:https://www.cnblogs.com/milianke/p/18351347