上期讲解到udp协议的基本要素https://blog.csdn.net/bingbang0703/article/details/134859060https://blog.csdn.net/bingbang0703/article/details/134859060
本文主要是根据接口GMII进行编写的代码,如有需要的可以根据此代码进行更改成其他接口即可,大致都是一样的,因为写的都是些一部分的东西,只需要改变引脚。
废话不多说,直接上代码
发送端顶层:
`timescale 1ns / 1ps
//
// Engineer: 兵棒
// Create Date: 2023/12/05 16:29:20
// Module Name: UDP_out
// Description: UDP输出顶层控制 接口位GMII千兆网通信
// Attention:字节输出是按照每个部分的最高位开始输出,bit传输时按照最低位开始传输
//
module UDP_out#(
parameter FPGA_MAC_ADDR = 48'h01_00_00_00_00_01, // FPGA MAC地址
parameter FPGA_IP_ADDR = 32'hc0_a8_01_7b, // FPGA IP地址
parameter FPGA_UDP_PORT = 16'd0, // FPGA UDP端口号
parameter PS_MAC_ADDR = 48'h02_00_00_00_00_02, // 目的侧 MAC地址
parameter PS_IP_ADDR = 32'hc0_a8_01_66, // 目的侧 IP地址
parameter PS_UDP_PORT = 16'd0 // 目的侧 UDP端口号
)(
input clk, //125M 1000Mbps
input rst_n, //复位
input tx_start, //传输开始信号
input [7:0] data_i, //数据
input data_valid_i, //有效
input [15:0] data_length_i, //一帧数据长度
output [7:0] tx_data, //发送端口
output tx_en, //有效
output tx_er, //错误提示
output gtx_clk, //参考时钟
output reg tx_data_start, //数据传输开始
output reg tx_done //一帧传输结束信号
);
// 前导码
localparam ETH_PREAMBLE = 8'h55;
localparam ETH_SFD = 8'hd5;
//帧类型
localparam ETH_TYPE = 16'h0800;//以太网 IP数据报 类型
// IP首部参数
localparam IP_HEAD_VER = 4'h4;//版本
localparam IP_HEAD_LEN = 4'h5;//首部长度
localparam IP_HEAD_TOS = 8'h00;//区分服务
localparam IP_HEAD_ID = 16'h0000;//标识
localparam IP_HEAD_FLAG = 3'b0_0_0;//标志
localparam IP_HEAD_OFFSET = 13'd0;//片偏移
localparam IP_HEAD_TTL = 8'h40;//生存时间
localparam IP_HEAD_PROT = 8'd17;//协议
localparam IP_Optional = 16'd0;//可选字段
localparam IP_fill = 16'd0;//填充
reg [7:0] tx_data_o;
reg tx_en_o;
reg [3:0] state;
reg [3:0] lead_code_cnt;//前导码计数
reg [3:0] ps_mac_cnt;//接收端物理地址计数
reg [3:0] fpga_mac_cnt;//发送端物理地址计数
reg type_flag;
reg type_done;
reg [5:0] ip_head_cnt;//IP头部长度计数
reg [5:0] udp_head_cnt;//udp头部长度计数
reg [15:0] tx_data_cnt;//数据传输长度计数
reg [3:0] crc_cnt;//FCS
reg [3:0] delay_cnt;//帧间隔
reg crc_done;
wire ip_check_valid;//校验和有效信号
wire [15:0] ip_checknum;//校验和
wire [15:0] IP_CHECK;
wire [7:0] PS_MAC[5:0];//接收端物理地址寄存
wire [7:0] FPGA_MAC[5:0];//发送端物理地址寄存
wire [15:0] IP_LENGTH;//IP总长度
wire [15:0] UDP_LENGTH;//udp总长度
wire [15:0] UDP_CHECK;//udp校验值
wire [31:0] CRC32_DATA_O;//FCS校验
wire I_CRC_EN;
assign tx_data=tx_data_o;
assign tx_en=tx_en_o;
assign tx_er=1'd0;
assign gtx_clk=clk;
assign IP_LENGTH=data_length_i+16'd8+16'd20;//总长度
assign UDP_LENGTH=data_length_i+16'd8;//UDP长度
assign UDP_CHECK=16'd0;//UDP校验值
assign IP_CHECK=(ip_check_valid) ? ip_checknum : IP_CHECK;
assign PS_MAC [0] = PS_MAC_ADDR[47:40];//接收端物理地址高位
assign PS_MAC [1] = PS_MAC_ADDR[39:32];
assign PS_MAC [2] = PS_MAC_ADDR[31:24];
assign PS_MAC [3] = PS_MAC_ADDR[23:16];
assign PS_MAC [4] = PS_MAC_ADDR[15:8 ];
assign PS_MAC [5] = PS_MAC_ADDR[7 :0 ];//接收端物理地址低位
assign FPGA_MAC[0] = FPGA_MAC_ADDR[47:40];//发送端物理地址高位
assign FPGA_MAC[1] = FPGA_MAC_ADDR[39:32];
assign FPGA_MAC[2] = FPGA_MAC_ADDR[31:24];
assign FPGA_MAC[3] = FPGA_MAC_ADDR[23:16];
assign FPGA_MAC[4] = FPGA_MAC_ADDR[15:8 ];
assign FPGA_MAC[5] = FPGA_MAC_ADDR[7 :0 ];//发送端物理地址低位
assign I_CRC_EN = (state>0&&state<9&&tx_en&&(!crc_done)) ? 1 : 0;
assign I_CRC_INIT = (state==0&&tx_start) ? 1 : 0;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
state <= 0;
lead_code_cnt <= 0;
tx_data_o <= 0;
ps_mac_cnt <= 0;
fpga_mac_cnt <= 0;
type_flag <= 0;
type_done <= 0;
ip_head_cnt <= 0;
udp_head_cnt <= 0;
tx_data_cnt <= 0;
crc_cnt <= 0;
delay_cnt <= 0;
tx_data_start <= 0;
tx_done <= 0;
crc_done <= 0;
end
else case (state)
0:begin
lead_code_cnt <= 0;
tx_data_o <= 0;
ps_mac_cnt <= 0;
fpga_mac_cnt <= 0;
type_flag <= 0;
type_done <= 0;
ip_head_cnt <= 0;
udp_head_cnt <= 0;
tx_data_cnt <= 0;
crc_cnt <= 0;
delay_cnt <= 0;
tx_data_start <= 0;
tx_done <= 0;
crc_done <= 0;
if(tx_start)
state <= 1;
else
state <= 0;
end
//前导码+帧开始
1:begin
if(lead_code_cnt<=4'd6)begin
lead_code_cnt <= lead_code_cnt + 1;
tx_data_o <= ETH_PREAMBLE;
end
else begin
lead_code_cnt <= lead_code_cnt;
tx_data_o <= ETH_SFD;
end
if(lead_code_cnt > 4'd6)
state <= 2;
else
state <= 1;
end
//目的物理地址
2:begin
lead_code_cnt <= 0;
tx_data_o <= PS_MAC[ps_mac_cnt];
if(ps_mac_cnt < 4'd5)begin
ps_mac_cnt <= ps_mac_cnt + 1;
end
else begin
ps_mac_cnt <= ps_mac_cnt;
end
if(ps_mac_cnt >= 4'd5)
state <= 3;
else
state <= 2;
end
//源物理地址
3:begin
ps_mac_cnt <= 0;
tx_data_o <= FPGA_MAC[fpga_mac_cnt];
type_flag <= 1;
if(fpga_mac_cnt < 4'd5)begin
fpga_mac_cnt<= fpga_mac_cnt + 1;
end
else begin
fpga_mac_cnt<= fpga_mac_cnt;
end
if(fpga_mac_cnt >= 4'd5)
state <= 4;
else
state <= 3;
end
//帧类型
4:begin
fpga_mac_cnt <= 0;
if(type_flag)begin
tx_data_o <= ETH_TYPE[15:8];
type_done <= 1;
end
if(type_done)begin
tx_data_o <= ETH_TYPE[7:0];
type_flag <= 0;
type_done <= 0;
state <= 5;
end
else
state <= 4;
end
//IP报头
5:begin
ip_head_cnt <= ip_head_cnt + 1;
case (ip_head_cnt)
0:tx_data_o <= {IP_HEAD_VER,IP_HEAD_LEN};
1:tx_data_o <= IP_HEAD_TOS;
2:tx_data_o <= IP_LENGTH[15:8];
3:tx_data_o <= IP_LENGTH[7:0];
4:tx_data_o <= IP_HEAD_ID[15:8];
5:tx_data_o <= IP_HEAD_ID[7:0];
6:tx_data_o <= {IP_HEAD_FLAG,IP_HEAD_OFFSET[12:8]};
7:tx_data_o <= IP_HEAD_OFFSET[7:0];
8:tx_data_o <= IP_HEAD_TTL;
9:tx_data_o <= IP_HEAD_PROT;
10:tx_data_o <= IP_CHECK[15:8];
11:tx_data_o <= IP_CHECK[7:0];
12:tx_data_o <= FPGA_IP_ADDR[31:24];
13:tx_data_o <= FPGA_IP_ADDR[23:16];
14:tx_data_o <= FPGA_IP_ADDR[15:8];
15:tx_data_o <= FPGA_IP_ADDR[7:0];
16:tx_data_o <= PS_IP_ADDR[31:24];
17:tx_data_o <= PS_IP_ADDR[23:16];
18:tx_data_o <= PS_IP_ADDR[15:8];
19:begin
tx_data_o <= PS_IP_ADDR[7:0];
state <= 6;
end
default:tx_data_o <= 8'd0;
endcase
end
//udp报头
6:begin
ip_head_cnt <= 0;
udp_head_cnt <= udp_head_cnt + 1;
case (udp_head_cnt)
0:tx_data_o <= FPGA_UDP_PORT[15:8];
1:tx_data_o <= FPGA_UDP_PORT[7:0];
2:tx_data_o <= PS_UDP_PORT[15:8];
3:tx_data_o <= PS_UDP_PORT[7:0];
4:tx_data_o <= UDP_LENGTH[15:8];
5:tx_data_o <= UDP_LENGTH[7:0];
6:tx_data_o <= UDP_CHECK[15:8];
7:begin
tx_data_o <= UDP_CHECK[7:0];
tx_data_start<= 1;
state <= 7;
end
default:tx_data_o <= 0;
endcase
end
//udp报文(数据)
7:begin
udp_head_cnt <= 0;
tx_data_start <= 0;
tx_data_o <= data_i;
if(tx_data_cnt==data_length_i-1)begin
tx_data_cnt <= 0;
state <= 8;
end
else begin
tx_data_cnt <= tx_data_cnt + 1;
state <= 7;
end
end
//FCS检错
8:begin
crc_done <=1;
crc_cnt <= crc_cnt + 1;
case (crc_cnt)
0:tx_data_o <= CRC32_DATA_O[31:24];
1:tx_data_o <= CRC32_DATA_O[23:16];
2:tx_data_o <= CRC32_DATA_O[15:8];
3:begin
tx_data_o <= CRC32_DATA_O[7:0];
state <= 9;
end
default:tx_data_o <= 0;
endcase
end
//帧间隔12个字节长
9:begin
crc_done <= 0;
crc_cnt <= 0;
tx_data_o <= 0;
if(delay_cnt==4'd11)begin
delay_cnt <= 0;
tx_done <= 1;
state <= 0;
end
else begin
delay_cnt <= delay_cnt + 1;
state <= 9;
end
end
default:begin
state <= 0;
lead_code_cnt <= 0;
tx_data_o <= 0;
ps_mac_cnt <= 0;
fpga_mac_cnt <= 0;
type_flag <= 0;
type_done <= 0;
ip_head_cnt <= 0;
udp_head_cnt <= 0;
tx_data_cnt <= 0;
crc_cnt <= 0;
delay_cnt <= 0;
tx_done <= 0;
tx_data_start <= 0;
crc_done <= 0;
end
endcase
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
tx_en_o <= 0;
end
else case (state)
1,2,3,4,5,6,7,8:tx_en_o <= 1;
0,9:tx_en_o <= 0;
default: tx_en_o <= 0;
endcase
end
CRC32_D8 CRC32_D8_out(
.I_OPR_CLK (clk ),
.I_OPR_RSTN (rst_n ),
.I_CRC_INIT (I_CRC_INIT ),
.I_CRC_EN (I_CRC_EN ),
.I_DATA (tx_data_o ),
.O_CRC_RES (CRC32_DATA_O )
);
check_num chek_num_out(
.IP_SA (FPGA_IP_ADDR ),
.IP_DA (PS_IP_ADDR ),
.clk (clk ),
.rst_n (rst_n ),
.check_start (tx_start ),
.ip_versions (IP_HEAD_VER ),
.ip_head_length (IP_HEAD_LEN ),
.ip_diffserv (IP_HEAD_TOS ),
.ip_all_length (IP_LENGTH ),
.ip_tag (IP_HEAD_ID ),
.ip_logo (IP_HEAD_FLAG ),
.ip_sheet (IP_HEAD_OFFSET ),
.ip_live (IP_HEAD_TTL ),
.ip_agreement (IP_HEAD_PROT ),
.ip_Optional (IP_Optional ),
.ip_fill (IP_fill ),
.ip_check_valid (ip_check_valid ),
.ip_checknum (ip_checknum )
);
endmodule
FCS检错代码:
// Engineer: 兵棒
// Create Date: 2023/12/04 16:29:20
// Module Name: CRC32_D8
// Description: FCS检错值
// Attention:代码生成网址http://crctool.easics.be/,然后进行一定的更改
module CRC32_D8(
// 输入输出端口
input I_OPR_CLK,
input I_OPR_RSTN,
input I_CRC_INIT,
input I_CRC_EN,
input [7:0] I_DATA,
output [31:0] O_CRC_RES
);
// 内部信号
wire [7:0] W_DATA;
reg [31:0] R_CRC_RES;
genvar GV_8;
generate
for(GV_8 = 0;GV_8 < 8;GV_8 = GV_8 + 1)begin
assign W_DATA[GV_8] = I_DATA[7-GV_8];
end
endgenerate
always @ (posedge I_OPR_CLK)begin
if(!I_OPR_RSTN)begin
R_CRC_RES <= {32{1'b1}};
end
else if(I_CRC_INIT)begin
R_CRC_RES <= {32{1'b1}};
end
else if(I_CRC_EN)begin
R_CRC_RES <= nextCRC32_D8(W_DATA,R_CRC_RES);
end
end
genvar GV_32;
generate
for(GV_32 = 0;GV_32 < 32;GV_32 = GV_32 + 1)begin
assign O_CRC_RES[GV_32] = ~R_CRC_RES[31-GV_32];
end
endgenerate
// polynomial: x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1
// data width: 8
// convention: the first serial bit is D[7]
function [31:0] nextCRC32_D8;
input [7:0] Data;
input [31:0] crc;
reg [7:0] d;
reg [31:0] c;
reg [31:0] newcrc;
begin
d = Data;
c = crc;
newcrc[0] = d[6] ^ d[0] ^ c[24] ^ c[30];
newcrc[1] = d[7] ^ d[6] ^ d[1] ^ d[0] ^ c[24] ^ c[25] ^ c[30] ^ c[31];
newcrc[2] = d[7] ^ d[6] ^ d[2] ^ d[1] ^ d[0] ^ c[24] ^ c[25] ^ c[26] ^ c[30] ^ c[31];
newcrc[3] = d[7] ^ d[3] ^ d[2] ^ d[1] ^ c[25] ^ c[26] ^ c[27] ^ c[31];
newcrc[4] = d[6] ^ d[4] ^ d[3] ^ d[2] ^ d[0] ^ c[24] ^ c[26] ^ c[27] ^ c[28] ^ c[30];
newcrc[5] = d[7] ^ d[6] ^ d[5] ^ d[4] ^ d[3] ^ d[1] ^ d[0] ^ c[24] ^ c[25] ^ c[27] ^ c[28] ^ c[29] ^ c[30] ^ c[31];
newcrc[6] = d[7] ^ d[6] ^ d[5] ^ d[4] ^ d[2] ^ d[1] ^ c[25] ^ c[26] ^ c[28] ^ c[29] ^ c[30] ^ c[31];
newcrc[7] = d[7] ^ d[5] ^ d[3] ^ d[2] ^ d[0] ^ c[24] ^ c[26] ^ c[27] ^ c[29] ^ c[31];
newcrc[8] = d[4] ^ d[3] ^ d[1] ^ d[0] ^ c[0] ^ c[24] ^ c[25] ^ c[27] ^ c[28];
newcrc[9] = d[5] ^ d[4] ^ d[2] ^ d[1] ^ c[1] ^ c[25] ^ c[26] ^ c[28] ^ c[29];
newcrc[10] = d[5] ^ d[3] ^ d[2] ^ d[0] ^ c[2] ^ c[24] ^ c[26] ^ c[27] ^ c[29];
newcrc[11] = d[4] ^ d[3] ^ d[1] ^ d[0] ^ c[3] ^ c[24] ^ c[25] ^ c[27] ^ c[28];
newcrc[12] = d[6] ^ d[5] ^ d[4] ^ d[2] ^ d[1] ^ d[0] ^ c[4] ^ c[24] ^ c[25] ^ c[26] ^ c[28] ^ c[29] ^ c[30];
newcrc[13] = d[7] ^ d[6] ^ d[5] ^ d[3] ^ d[2] ^ d[1] ^ c[5] ^ c[25] ^ c[26] ^ c[27] ^ c[29] ^ c[30] ^ c[31];
newcrc[14] = d[7] ^ d[6] ^ d[4] ^ d[3] ^ d[2] ^ c[6] ^ c[26] ^ c[27] ^ c[28] ^ c[30] ^ c[31];
newcrc[15] = d[7] ^ d[5] ^ d[4] ^ d[3] ^ c[7] ^ c[27] ^ c[28] ^ c[29] ^ c[31];
newcrc[16] = d[5] ^ d[4] ^ d[0] ^ c[8] ^ c[24] ^ c[28] ^ c[29];
newcrc[17] = d[6] ^ d[5] ^ d[1] ^ c[9] ^ c[25] ^ c[29] ^ c[30];
newcrc[18] = d[7] ^ d[6] ^ d[2] ^ c[10] ^ c[26] ^ c[30] ^ c[31];
newcrc[19] = d[7] ^ d[3] ^ c[11] ^ c[27] ^ c[31];
newcrc[20] = d[4] ^ c[12] ^ c[28];
newcrc[21] = d[5] ^ c[13] ^ c[29];
newcrc[22] = d[0] ^ c[14] ^ c[24];
newcrc[23] = d[6] ^ d[1] ^ d[0] ^ c[15] ^ c[24] ^ c[25] ^ c[30];
newcrc[24] = d[7] ^ d[2] ^ d[1] ^ c[16] ^ c[25] ^ c[26] ^ c[31];
newcrc[25] = d[3] ^ d[2] ^ c[17] ^ c[26] ^ c[27];
newcrc[26] = d[6] ^ d[4] ^ d[3] ^ d[0] ^ c[18] ^ c[24] ^ c[27] ^ c[28] ^ c[30];
newcrc[27] = d[7] ^ d[5] ^ d[4] ^ d[1] ^ c[19] ^ c[25] ^ c[28] ^ c[29] ^ c[31];
newcrc[28] = d[6] ^ d[5] ^ d[2] ^ c[20] ^ c[26] ^ c[29] ^ c[30];
newcrc[29] = d[7] ^ d[6] ^ d[3] ^ c[21] ^ c[27] ^ c[30] ^ c[31];
newcrc[30] = d[7] ^ d[4] ^ c[22] ^ c[28] ^ c[31];
newcrc[31] = d[5] ^ c[23] ^ c[29];
nextCRC32_D8 = newcrc;
end
endfunction
endmodule
IP首部检验和代码:
`timescale 1ns / 1ps
//
// Engineer: 兵棒
// Create Date: 2023/12/05 14:28:20
// Module Name: check_num
// Description: IP首部检验和的计算
// 计算公式:
// 首部检验和= ~({版本,首部长度,区分服务}+总长度+标识+{标志,片偏移}+{生存时间,协议}
// +源地址前16位+目的地址前16位+源地址后16位+目的地址后16位)
//
module check_num(
input clk,
input rst_n,
input check_start,
input [31:0] IP_SA,//192.168.1.123源地址
input [31:0] IP_DA,//192.168.1.102目的地址
input [3:0] ip_versions, //版本
input [3:0] ip_head_length, //头部长度
input [7:0] ip_diffserv, //区分服务
input [15:0] ip_all_length, //总长度
input [15:0] ip_tag, //标识
input [2:0] ip_logo, //标志
input [12:0] ip_sheet, //片偏移
input [7:0] ip_live, //生存时间
input [7:0] ip_agreement, //协议
input [15:0] ip_Optional, //可选字段
input [15:0] ip_fill, //填充
output ip_check_valid, //首部检验和有效
output [15:0] ip_checknum //首部检验和
);
reg [31:0] ip_checknum_t;
reg [15:0] ip_check_over;
reg [15:0] ip_check;
reg [3:0] state;
reg valid;
wire [15:0] num_1;
wire [15:0] num_2;
wire [15:0] num_3;
wire [15:0] num_4;
wire [15:0] num_5;
wire [15:0] num_6;
wire [15:0] num_7;
assign num_1={ip_versions,ip_head_length,ip_diffserv};//版本+首部长度+区分服务
assign num_2={ip_logo,ip_sheet};//标志+片偏移
assign num_3={ip_live,ip_agreement};//生存时间+协议
assign num_4=IP_SA[31:16];//源地址前16位
assign num_5=IP_SA[15:0 ];//源地址后16位
assign num_6=IP_DA[31:16];//目的地址后16位
assign num_7=IP_DA[15:0 ];//目的地址后16位
assign ip_checknum=~ip_check;
assign ip_check_valid=valid;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
ip_checknum_t <= 0;
ip_check_over <= 0;
ip_check <= 0;
valid <= 0;
state <= 0;
end
else case(state)
0:begin
ip_checknum_t <= 0;
ip_check_over <= 0;
ip_check <= 0;
valid <= 0;
if(check_start)
state <= 1;
else
state <= 0;
end
1:begin
ip_checknum_t <= num_1+ip_all_length+ip_tag+num_2+num_3+num_4+num_5+num_6+num_7+ip_Optional+ip_fill;
state <= 2;
end
2:begin
ip_check_over <= ip_checknum_t[31:16];
state <= 3;
end
3:begin
ip_checknum_t[31:16]<=16'h0;
if(ip_check_over>0)begin
state <= 4;
end
else begin
state <= 5;
end
end
4:begin
ip_checknum_t <= ip_check_over+ip_checknum_t[15:0];
if(ip_checknum_t[31:16]>0)begin
state <= 3;
ip_check_over<= ip_checknum_t[31:16];
end
else begin
state <= 5;
ip_check_over<= ip_check_over;
end
end
5:begin
ip_check <= ip_checknum_t[15:0];
valid <= 1;
state <= 0;
end
default:begin
ip_checknum_t <= 0;
ip_check <= 0;
ip_check_over <= 0;
valid <= 0;
state <= 0;
end
endcase
end
endmodule
仿真代码:
`timescale 1ns / 1ps
//
// Engineer: 兵棒
// Create Date: 2023/12/05 15:32:15
// Module Name: tb_check
// Description: 仿真
//
module tb_check();
reg clk;
reg rst_n;
//out
reg tx_start;
reg [7:0] data_i;
reg data_valid_i;
reg [15:0] data_length_i;
wire [7:0] tx_data;
wire tx_en;
wire tx_er;
wire gtx_clk;
wire tx_data_start;
wire tx_done ;
always #4 clk=~clk;
initial
begin
clk=0;rst_n=0;tx_start=0;data_length_i=16'd0;tx_wait=0;
#50 rst_n=1;
#50 tx_start=1;
#8 tx_start=0;data_length_i=16'd30;
end
always@(posedge clk)begin
if(!rst_n)
data_i<=0;
else if(tx_data_start)
data_i<=data_i+1;
else
data_i<=data_i;
end
UDP_out tb_udp_out(
.clk (clk ) ,
.rst_n (rst_n ) ,
.tx_start (tx_start ) ,
.data_i (data_i ) ,
.data_valid_i (data_valid_i ) ,
.data_length_i(data_length_i) ,
.tx_data (tx_data ) ,
.tx_en (tx_en ) ,
.tx_er (tx_er ) ,
.gtx_clk (gtx_clk ) ,
.tx_data_start(tx_data_start) ,
.tx_done (tx_done )
);
仿真结果
从仿真数据中可以看到相对位的数据都是正常输出的