初级会员
- 积分
- 92
- 金钱
- 92
- 注册时间
- 2017-8-17
- 在线时间
- 50 小时
|
使用达芬奇FPGA开发板上的千兆网口测试UDP通信速率。将原子核提供的UDP回环例程改了一下。改成10S内持续由FPGA千兆网口发送数据至PC端。经实测发现,每个包数据大小为1401字节,10秒只接收到了4419个包,开始的时候包没有丢失,后面数据包有间隔丢失,具体开始丢失的位置没有花时间去找。在代码里每发一个包会添加1us的时间延时,不然的话实测速率就只有几十Kbit/s了,而且从第二个包开始就丢了几千个包了,所以包之间加了延时。不太明白这里速率的瓶颈被什么给限制住了,已经可以确定PC是千兆网口。这里将做了更改的代码文件上传一下。其他不变的同原子哥提供的例程(eth_udp_loop)一样。
module eth_udp_loop(
input sys_clk , //系统时钟
input sys_rst_n , //系统复位信号,低电平有效
input [1:0] key ,
//以太网RGMII接口
input eth_rxc , //RGMII接收数据时钟
input eth_rx_ctl, //RGMII输入数据有效信号
input [3:0] eth_rxd , //RGMII输入数据
output eth_txc , //RGMII发送数据时钟
output eth_tx_ctl, //RGMII输出数据有效信号
output [3:0] eth_txd , //RGMII输出数据
output eth_rst_n //以太网芯片复位信号,低电平有效
);
//parameter define
//开发板MAC地址 00-11-22-33-44-55
parameter BOARD_MAC = 48'h00_11_22_33_44_55;
//开发板IP地址 192.168.1.10
parameter BOARD_IP = {8'd192,8'd168,8'd1,8'd10};
//目的MAC地址 ff_ff_ff_ff_ff_ff
parameter DES_MAC = 48'hff_ff_ff_ff_ff_ff;
//目的IP地址 192.168.1.102
parameter DES_IP = {8'd192,8'd168,8'd1,8'd102};
//输入数据IO延时,此处为0,即不延时(如果为n,表示延时n*78ps)
parameter IDELAY_VALUE = 0;
//wire define
wire clk_200m ; //用于IO延时的时钟
wire gmii_rx_clk; //GMII接收时钟
wire gmii_rx_dv ; //GMII接收数据有效信号
wire [7:0] gmii_rxd ; //GMII接收数据
wire gmii_tx_clk; //GMII发送时钟
wire gmii_tx_en ; //GMII发送数据使能信号
wire [7:0] gmii_txd ; //GMII发送数据
wire arp_gmii_tx_en; //ARP GMII输出数据有效信号
wire [7:0] arp_gmii_txd ; //ARP GMII输出数据
wire arp_rx_done ; //ARP接收完成信号
wire arp_rx_type ; //ARP接收类型 0:请求 1:应答
wire [47:0] src_mac ; //接收到目的MAC地址
wire [31:0] src_ip ; //接收到目的IP地址
wire arp_tx_en ; //ARP发送使能信号
wire arp_tx_type ; //ARP发送类型 0:请求 1:应答
wire [47:0] des_mac ; //发送的目标MAC地址
wire [31:0] des_ip ; //发送的目标IP地址
wire arp_tx_done ; //ARP发送完成信号
wire udp_gmii_tx_en; //UDP GMII输出数据有效信号
wire [7:0] udp_gmii_txd ; //UDP GMII输出数据
wire rec_pkt_done ; //UDP单包数据接收完成信号
reg rec_en ; //UDP接收的数据使能信号
reg [31:0] rec_data ; //UDP接收的数据
wire [15:0] rec_byte_num ; //UDP接收的有效字节数 单位:byte
wire [15:0] tx_byte_num ; //UDP发送的有效字节数 单位:byte
wire udp_tx_done ; //UDP发送完成信号
wire tx_req ; //UDP读数据请求信号
wire [31:0] tx_data ; //UDP待发送数据
reg tx_start_en;
reg key_flag;
reg [10:0] delay;
reg [31:0] time_10s;
reg [31:0] delay1;
//*****************************************************
//** main code
//*****************************************************
//assign tx_start_en = rec_pkt_done;
assign tx_byte_num = 12'd1401;
assign des_mac = src_mac;
assign des_ip = src_ip;
assign eth_rst_n = sys_rst_n;
//按键0按下启动网口数据发送,按键1按下停止网口数据发送
always @(posedge eth_txc or negedge sys_rst_n) begin
if(!sys_rst_n) begin
tx_start_en <= 1'b0;
time_10s<=32'd0;
delay1<=32'd0;
end
else if((key_flag==1)&&(time_10s<32'd1250000000))
begin
time_10s<=time_10s+1'b1;
tx_start_en <= 1'b1;
if(udp_tx_done)
begin
tx_start_en <= 1'b0;
end
if(~tx_start_en)
begin
delay1<=delay1+1'b1;
if(delay1>125)//1us
begin
delay1<=32'd0;
tx_start_en<=1'b1;
end
end
end
else if((key_flag==0)||(time_10s>=32'd1250000000))
begin
tx_start_en <= 1'b0;
end
end
always @(posedge eth_txc or negedge sys_rst_n)
begin
if(!sys_rst_n)
begin
key_flag<=1'b0;
delay<=10'd0;
end
else if(key[0]==0)
begin
delay<=delay+1'b1;
if((delay>=500)&&(key[0]==0))
begin
key_flag<=1'b1;
delay<=10'd0;
end
end
else if(key[1]==0)
begin
delay<=delay+1'b1;
if((delay>=500)&&(key[1]==0))
begin
key_flag<=1'b0;
delay<=10'd0;
end
end
end
//模拟数据
always @(posedge eth_txc or negedge sys_rst_n)
begin
if(!sys_rst_n) begin
rec_data <= 32'b0;
rec_en<=1'b0;
end
else if(tx_start_en==1)
begin
rec_data <= rec_data+1'b1;
if(rec_data%4==0)
begin
rec_en<=1'b1;
end
else
begin
rec_en<=1'b0;
end
end
end
//MMCM/PLL
clk_wiz u_clk_wiz
(
.clk_in1 (sys_clk ),
.clk_out1 (clk_200m ),
.reset (~sys_rst_n),
.locked (locked)
);
//GMII接口转RGMII接口
gmii_to_rgmii
#(
.IDELAY_VALUE (IDELAY_VALUE)
)
u_gmii_to_rgmii(
.idelay_clk (clk_200m ),
.gmii_rx_clk (gmii_rx_clk ),
.gmii_rx_dv (gmii_rx_dv ),
.gmii_rxd (gmii_rxd ),
.gmii_tx_clk (gmii_tx_clk ),
.gmii_tx_en (gmii_tx_en ),
.gmii_txd (gmii_txd ),
.rgmii_rxc (eth_rxc ),
.rgmii_rx_ctl (eth_rx_ctl ),
.rgmii_rxd (eth_rxd ),
.rgmii_txc (eth_txc ),
.rgmii_tx_ctl (eth_tx_ctl ),
.rgmii_txd (eth_txd )
);
//ARP通信
arp
#(
.BOARD_MAC (BOARD_MAC), //参数例化
.BOARD_IP (BOARD_IP ),
.DES_MAC (DES_MAC ),
.DES_IP (DES_IP )
)
u_arp(
.rst_n (sys_rst_n ),
.gmii_rx_clk (gmii_rx_clk),
.gmii_rx_dv (gmii_rx_dv ),
.gmii_rxd (gmii_rxd ),
.gmii_tx_clk (gmii_tx_clk),
.gmii_tx_en (arp_gmii_tx_en ),
.gmii_txd (arp_gmii_txd),
.arp_rx_done (arp_rx_done),
.arp_rx_type (arp_rx_type),
.src_mac (src_mac ),
.src_ip (src_ip ),
.arp_tx_en (arp_tx_en ),
.arp_tx_type (arp_tx_type),
.des_mac (des_mac ),
.des_ip (des_ip ),
.tx_done (arp_tx_done)
);
//UDP通信
udp
#(
.BOARD_MAC (BOARD_MAC), //参数例化
.BOARD_IP (BOARD_IP ),
.DES_MAC (DES_MAC ),
.DES_IP (DES_IP )
)
u_udp(
.rst_n (sys_rst_n ),
.gmii_rx_clk (gmii_rx_clk ),
.gmii_rx_dv (gmii_rx_dv ),
.gmii_rxd (gmii_rxd ),
.gmii_tx_clk (gmii_tx_clk ),
.gmii_tx_en (udp_gmii_tx_en),
.gmii_txd (udp_gmii_txd),
.rec_pkt_done (rec_pkt_done),
.rec_en ( ),
.rec_data ( ),
.rec_byte_num (rec_byte_num),
.tx_start_en (tx_start_en ),
.tx_data (tx_data ),
.tx_byte_num (tx_byte_num ),
.des_mac (des_mac ),
.des_ip (des_ip ),
.tx_done (udp_tx_done ),
.tx_req (tx_req )
);
//同步FIFO
sync_fifo_2048x32b u_sync_fifo_2048x32b (
.clk (gmii_rx_clk), // input wire clk
.rst (~sys_rst_n), // input wire rst
.din (rec_data ), // input wire [31 : 0] din
.wr_en (rec_en ), // input wire wr_en
.rd_en (tx_req ), // input wire rd_en
.dout (tx_data ), // output wire [31 : 0] dout
.full (), // output wire full
.empty () // output wire empty
);
//以太网控制模块
eth_ctrl u_eth_ctrl(
.clk (gmii_rx_clk),
.rst_n (sys_rst_n),
.arp_rx_done (arp_rx_done ),
.arp_rx_type (arp_rx_type ),
.arp_tx_en (arp_tx_en ),
.arp_tx_type (arp_tx_type ),
.arp_tx_done (arp_tx_done ),
.arp_gmii_tx_en (arp_gmii_tx_en),
.arp_gmii_txd (arp_gmii_txd ),
.udp_gmii_tx_en (udp_gmii_tx_en),
.udp_gmii_txd (udp_gmii_txd ),
.gmii_tx_en (gmii_tx_en ),
.gmii_txd (gmii_txd )
);
endmodule
第二个文件代码
module udp_tx(
input clk , //时钟信号
input rst_n , //复位信号,低电平有效
input tx_start_en, //以太网开始发送信号
// input [31:0] tx_data , //以太网待发送数据
input [15:0] tx_byte_num, //以太网发送的有效字节数
input [47:0] des_mac , //发送的目标MAC地址
input [31:0] des_ip , //发送的目标IP地址
input [31:0] crc_data , //CRC校验数据
input [7:0] crc_next , //CRC下次校验完成数据
output reg tx_done , //以太网发送完成信号
output reg tx_req , //读数据请求信号
output reg gmii_tx_en , //GMII输出数据有效信号
output reg [7:0] gmii_txd , //GMII输出数据
output reg crc_en , //CRC开始校验使能
output reg crc_clr //CRC数据复位信号
);
//parameter define
//开发板MAC地址 00-11-22-33-44-55
parameter BOARD_MAC = 48'h00_11_22_33_44_55;
//开发板IP地址 192.168.1.10
parameter BOARD_IP = {8'd192,8'd168,8'd1,8'd10};
//目的MAC地址 ff_ff_ff_ff_ff_ff
parameter DES_MAC = 48'hff_ff_ff_ff_ff_ff;
//目的IP地址 192.168.1.102
parameter DES_IP = {8'd192,8'd168,8'd1,8'd102};
localparam st_idle = 7'b000_0001; //初始状态,等待开始发送信号
localparam st_check_sum = 7'b000_0010; //IP首部校验和
localparam st_preamble = 7'b000_0100; //发送前导码+帧起始界定符
localparam st_eth_head = 7'b000_1000; //发送以太网帧头
localparam st_ip_head = 7'b001_0000; //发送IP首部+UDP首部
localparam st_tx_data = 7'b010_0000; //发送数据
localparam st_crc = 7'b100_0000; //发送CRC校验值
localparam ETH_TYPE = 16'h0800 ; //以太网协议类型 IP协议
//以太网数据最小46个字节,IP首部20个字节+UDP首部8个字节
//所以数据至少46-20-8=18个字节
localparam MIN_DATA_NUM = 16'd18 ;
localparam UDP_TYPE = 8'd17 ; //UDP协议类型
reg [31:0] tx_data;
//reg define
reg [6:0] cur_state ;
reg [6:0] next_state ;
reg [7:0] preamble[7:0] ; //前导码
reg [7:0] eth_head[13:0] ; //以太网首部
reg [31:0] ip_head[6:0] ; //IP首部 + UDP首部
reg start_en_d0 ;
reg start_en_d1 ;
reg [15:0] tx_data_num ; //发送的有效数据字节个数
reg [15:0] total_num ; //总字节数
reg trig_tx_en ;
reg [15:0] udp_num ; //UDP字节数
reg skip_en ; //控制状态跳转使能信号
reg [4:0] cnt ;
reg [31:0] check_buffer ; //首部校验和
reg [1:0] tx_byte_sel ; //32位数据转8位数据计数器
reg [15:0] data_cnt ; //发送数据个数计数器
reg tx_done_t ;
reg [4:0] real_add_cnt ; //以太网数据实际多发的字节数
//wire define
wire pos_start_en ;//开始发送数据上升沿
wire [15:0] real_tx_data_num;//实际发送的字节数(以太网最少字节要求)
//*****************************************************
//** main code
//*****************************************************
assign pos_start_en = (~start_en_d1) & start_en_d0;
assign real_tx_data_num = (tx_data_num >= MIN_DATA_NUM)
? tx_data_num : MIN_DATA_NUM;
//采tx_start_en的上升沿
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
start_en_d0 <= 1'b0;
start_en_d1 <= 1'b0;
end
else begin
start_en_d0 <= tx_start_en;
start_en_d1 <= start_en_d0;
end
end
//寄存数据有效字节
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
tx_data_num <= 16'd0;
total_num <= 16'd0;
udp_num <= 16'd0;
end
else begin
if(pos_start_en && cur_state==st_idle) begin
//数据长度
tx_data_num <= tx_byte_num;
//UDP长度:UDP首部长度 + 有效数据
udp_num <= tx_byte_num + 16'd8;
//IP长度:IP首部长度 + UDP首部 + 有效数据
total_num <= tx_byte_num + 16'd20 + 16'd8;
end
end
end
//触发发送信号
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
trig_tx_en <= 1'b0;
else
trig_tx_en <= pos_start_en;
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
cur_state <= st_idle;
else
cur_state <= next_state;
end
always @(*) begin
next_state = st_idle;
case(cur_state)
st_idle : begin //等待发送数据
if(skip_en)
next_state = st_check_sum;
else
next_state = st_idle;
end
st_check_sum: begin //IP首部校验
if(skip_en)
next_state = st_preamble;
else
next_state = st_check_sum;
end
st_preamble : begin //发送前导码+帧起始界定符
if(skip_en)
next_state = st_eth_head;
else
next_state = st_preamble;
end
st_eth_head : begin //发送以太网首部
if(skip_en)
next_state = st_ip_head;
else
next_state = st_eth_head;
end
st_ip_head : begin //发送IP首部+UDP首部
if(skip_en)
next_state = st_tx_data;
else
next_state = st_ip_head;
end
st_tx_data : begin //发送数据
if(skip_en)
next_state = st_crc;
else
next_state = st_tx_data;
end
st_crc: begin //发送CRC校验值
if(skip_en)
begin
next_state = st_idle;
end
else
next_state = st_crc;
end
default : next_state = st_idle;
endcase
end
//发送数据
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
skip_en <= 1'b0;
cnt <= 5'd0;
check_buffer <= 32'd0;
ip_head[1][31:16] <= 16'd0;
tx_byte_sel <= 2'b0;
crc_en <= 1'b0;
gmii_tx_en <= 1'b0;
gmii_txd <= 8'd0;
tx_req <= 1'b0;
tx_done_t <= 1'b0;
data_cnt <= 16'd0;
real_add_cnt <= 5'd0;
tx_data<=32'd0;
//初始化数组
//前导码 7个8'h55 + 1个8'hd5
preamble[0] <= 8'h55;
preamble[1] <= 8'h55;
preamble[2] <= 8'h55;
preamble[3] <= 8'h55;
preamble[4] <= 8'h55;
preamble[5] <= 8'h55;
preamble[6] <= 8'h55;
preamble[7] <= 8'hd5;
//目的MAC地址
eth_head[0] <= DES_MAC[47:40];
eth_head[1] <= DES_MAC[39:32];
eth_head[2] <= DES_MAC[31:24];
eth_head[3] <= DES_MAC[23:16];
eth_head[4] <= DES_MAC[15:8];
eth_head[5] <= DES_MAC[7:0];
//源MAC地址
eth_head[6] <= BOARD_MAC[47:40];
eth_head[7] <= BOARD_MAC[39:32];
eth_head[8] <= BOARD_MAC[31:24];
eth_head[9] <= BOARD_MAC[23:16];
eth_head[10] <= BOARD_MAC[15:8];
eth_head[11] <= BOARD_MAC[7:0];
//以太网类型
eth_head[12] <= ETH_TYPE[15:8];
eth_head[13] <= ETH_TYPE[7:0];
end
else begin
skip_en <= 1'b0;
tx_req <= 1'b0;
crc_en <= 1'b0;
gmii_tx_en <= 1'b0;
tx_done_t <= 1'b0;
case(next_state)
st_idle : begin
if(trig_tx_en) begin
tx_data<=tx_data+1'b1;
skip_en <= 1'b1;
//版本号:4 首部长度:5(单位:32bit,20byte/4=5)
ip_head[0] <= {8'h45,8'h00,total_num};
//16位标识,每次发送累加1
ip_head[1][31:16] <= ip_head[1][31:16] + 1'b1;
//bit[15:13]: 010表示不分片
ip_head[1][15:0] <= 16'h4000;
//协议:17(udp)
ip_head[2] <= {8'h40,UDP_TYPE,16'h0};
//源IP地址
ip_head[3] <= BOARD_IP;
//目的IP地址
if(des_ip != 32'd0)
ip_head[4] <= des_ip;
else
ip_head[4] <= DES_IP;
//16位源端口号:1234 16位目的端口号:8811
ip_head[5] <= {16'd8811,16'd8811};
//16位udp长度,16位udp校验和
ip_head[6] <= {udp_num,16'h0000};
//更新MAC地址
if(des_mac != 48'b0) begin
//目的MAC地址
eth_head[0] <= des_mac[47:40];
eth_head[1] <= des_mac[39:32];
eth_head[2] <= des_mac[31:24];
eth_head[3] <= des_mac[23:16];
eth_head[4] <= des_mac[15:8];
eth_head[5] <= des_mac[7:0];
end
end
end
st_check_sum: begin //IP首部校验
cnt <= cnt + 5'd1;
if(cnt == 5'd0) begin
check_buffer <= ip_head[0][31:16] + ip_head[0][15:0]
+ ip_head[1][31:16] + ip_head[1][15:0]
+ ip_head[2][31:16] + ip_head[2][15:0]
+ ip_head[3][31:16] + ip_head[3][15:0]
+ ip_head[4][31:16] + ip_head[4][15:0];
end
else if(cnt == 5'd1) //可能出现进位,累加一次
check_buffer <= check_buffer[31:16] + check_buffer[15:0];
else if(cnt == 5'd2) begin //可能再次出现进位,累加一次
check_buffer <= check_buffer[31:16] + check_buffer[15:0];
end
else if(cnt == 5'd3) begin //按位取反
skip_en <= 1'b1;
cnt <= 5'd0;
ip_head[2][15:0] <= ~check_buffer[15:0];
end
end
st_preamble : begin //发送前导码+帧起始界定符
gmii_tx_en <= 1'b1;
gmii_txd <= preamble[cnt];
if(cnt == 5'd7) begin
skip_en <= 1'b1;
cnt <= 5'd0;
end
else
cnt <= cnt + 5'd1;
end
st_eth_head : begin //发送以太网首部
gmii_tx_en <= 1'b1;
crc_en <= 1'b1;
gmii_txd <= eth_head[cnt];
if (cnt == 5'd13) begin
skip_en <= 1'b1;
cnt <= 5'd0;
end
else
cnt <= cnt + 5'd1;
end
st_ip_head : begin //发送IP首部 + UDP首部
crc_en <= 1'b1;
gmii_tx_en <= 1'b1;
tx_byte_sel <= tx_byte_sel + 2'd1;
if(tx_byte_sel == 2'd0)
gmii_txd <= ip_head[cnt][31:24];
else if(tx_byte_sel == 2'd1)
gmii_txd <= ip_head[cnt][23:16];
else if(tx_byte_sel == 2'd2) begin
gmii_txd <= ip_head[cnt][15:8];
if(cnt == 5'd6) begin
//提前读请求数据,等待数据有效时发送
tx_req <= 1'b1;
end
end
else if(tx_byte_sel == 2'd3) begin
gmii_txd <= ip_head[cnt][7:0];
if(cnt == 5'd6) begin
skip_en <= 1'b1;
cnt <= 5'd0;
end
else
cnt <= cnt + 5'd1;
end
end
st_tx_data : begin //发送数据
crc_en <= 1'b1;
gmii_tx_en <= 1'b1;
tx_byte_sel <= tx_byte_sel + 2'd1;
if(tx_byte_sel == 1'b0)
gmii_txd <= tx_data[31:24];
else if(tx_byte_sel == 2'd1)
gmii_txd <= tx_data[23:16];
else if(tx_byte_sel == 2'd2) begin
gmii_txd <= tx_data[15:8];
if(data_cnt != tx_data_num - 16'd2)
tx_req <= 1'b1;
end
else if(tx_byte_sel == 2'd3)
gmii_txd <= tx_data[7:0];
if(data_cnt < tx_data_num - 16'd1)
data_cnt <= data_cnt + 16'd1;
else if(data_cnt == tx_data_num - 16'd1)begin
//如果发送的有效数据少于18个字节,在后面填补充位
//补充的值为最后一次发送的有效数据
tx_req <= 1'b0;
if(data_cnt + real_add_cnt < real_tx_data_num - 16'd1)
real_add_cnt <= real_add_cnt + 5'd1;
else begin
skip_en <= 1'b1;
data_cnt <= 16'd0;
real_add_cnt <= 5'd0;
tx_byte_sel <= 2'd0;
end
if(real_add_cnt > 0) begin
gmii_txd <= 8'd0;
end
end
end
st_crc : begin //发送CRC校验值
gmii_tx_en <= 1'b1;
tx_byte_sel <= tx_byte_sel + 2'd1;
if(tx_byte_sel == 2'd0)
gmii_txd <= {~crc_next[0], ~crc_next[1], ~crc_next[2],~crc_next[3],
~crc_next[4], ~crc_next[5], ~crc_next[6],~crc_next[7]};
else if(tx_byte_sel == 2'd1)
gmii_txd <= {~crc_data[16], ~crc_data[17], ~crc_data[18],~crc_data[19],
~crc_data[20], ~crc_data[21], ~crc_data[22],~crc_data[23]};
else if(tx_byte_sel == 2'd2) begin
gmii_txd <= {~crc_data[8], ~crc_data[9], ~crc_data[10],~crc_data[11],
~crc_data[12], ~crc_data[13], ~crc_data[14],~crc_data[15]};
end
else if(tx_byte_sel == 2'd3) begin
gmii_txd <= {~crc_data[0], ~crc_data[1], ~crc_data[2],~crc_data[3],
~crc_data[4], ~crc_data[5], ~crc_data[6],~crc_data[7]};
tx_done_t <= 1'b1;
skip_en <= 1'b1;
end
end
default :;
endcase
end
end
//发送完成信号及crc值复位信号
always @(posedge clk or negedge rst_n)
begin
if(!rst_n) begin
tx_done <= 1'b0;
crc_clr <= 1'b0;
end
else
begin
tx_done <= tx_done_t;
crc_clr <= tx_done_t;
end
end
endmodule
|
-
网口调试助手截图
|