Verilog 实现千兆网UDP协议 基于88E1111–数据发送
注:此版本没有添加ARP PING 等,未完待续。
注:项目采用Verilog开发,基于Vivado编译器。
UDP(User Datagram Protocol)一种基本的,低延迟的数据报。本章简单的实现了UDP数据包的发送。
首先UDP协议通讯信息中包含:MAC地址,IP地址,端口地址等。这些信息在没有提供ARP模式时,需要提前数据,所以采用Verilog Head 文件,提前输入,方便修改,适配时,只需更改头文件。
`define Leading_code 64'h55_55_55_55_55_55_55_D5
`define Source_MAC 48'h11_22_33_44_55_01
`define Destination_MAC 48'h11_22_33_44_55_02
`define IP_TYPE 16'h08_00
`define IP_TYPE_ARP 16'h08_06
`define Source_IP 32'hC0_0A_00_02 //192.168.0.2
`define Destination_IP 32'hC0_0A_00_03 //192.168.0.3
`define Source_Port 16'h80_00
`define Destination_Port 16'h80_00
模块接口如下:架构为从外部FIFO提取数据写入,读取外部FIFO内数据量,设置阈值,当数据量大于阈值,将命令Eth_Command和数据长度Data_length同步写入模块,模块跳出idle状态,开始进行数据写入。
模块对外接口Eth_Write_Busy:拉高的时候,外部就等待,不要在写入命令。拉低时就可以再次对模块进行操作。
现在还没有完善为有数据就开始写入。所以Txen_o会在每包数据写入时持续拉高,Txer_o就没有用。
Txd_o就是数据写入接口。其他如字面意思。
input clk_i,
input rst_n,
/*eth interface*/
output reg Txen_o, //GMII数据使能信号
output Txer_o, //GMII发送错误信号
output reg [ 7:0] Txd_o,
/*control interface*/
input [ 3:0] Eth_Command,
input [15:0] Data_length,
output reg Eth_Write_Busy,
/*fifo interface*/
input [ 7:0] Fifo_Data_i,
output reg Fifo_en
采用三段式状态机开发,将各部分独立,方便单独信号维护,主要是习惯…
localparam idle = 8'b1111_1110; //fe
localparam Cal_Head_Crc = 8'b1111_1101; //fd
localparam Write_Leading = 8'b1111_1011; //fb
localparam Write_Header = 8'b1111_0111; //f7
localparam Write_Data = 8'b1110_1111; //ef
localparam Write_Crc = 8'b1101_1111; //df
localparam Write_Over = 8'b1011_1111; //bf
本模块将发送程序划分为6个状态机,
如命名所示:
状态1、准备数据
状态2、计算校验
状态3、写引导符:下面代码中的Leading_Code
状态4、写入头:有些固定写入就可以,具体代表什么见注释…下面代码中的Header_Code
状态5、写入数据:从FIFO中提取需要发送的数据。
状态6、写入校验和:具体见校验和计算部分
状态7、写完成:这个状态就对包计数加个1
状态跳转:三段式状态机中关键部分,判断Flag来实现状态切换
idle: nstate <= (Eth_Command == 4'hA )? Cal_Head_Crc : idle;
Cal_Head_Crc : nstate <= (State_turn) ? Write_Leading : Cal_Head_Crc;
Write_Leading: nstate <= (State_turn) ? Write_Header : Write_Leading;
Write_Header : nstate <= (State_turn) ? Write_Data : Write_Header;
Write_Data : nstate <= (State_turn) ? Write_Crc : Write_Data;
Write_Crc : nstate <= (State_turn) ? Write_Over : Write_Crc;
Write_Over : nstate <= idle;
default: nstate <= idle;
写入数据代码:就是在给Txd_o ,使能信号Txen_o单独维护,这也是三段式状态机的好处。
idle: begin
Leading_Code <= {`Leading_code,`Source_MAC,`Destination_MAC,`IP_TYPE}; //
Header_Code <= {16'h45_00 ,Total_length, // | 版本号4 | 头长度4 | 服务类型8 | 总长度16 |
Package_count,16'h40_00, // | 标识16 | 标志3|片偏移13 |
16'h80_11 ,16'h00_00, // | 生存时间TTL8 |上层协议标识8 | 头部校验和16 |
`Source_IP , // | 源IP地址32 |
`Destination_IP, // | 目标IP地址32 |
`Source_Port ,`Destination_Port, // | 选项 |
Data_length ,16'h00_00 // | |
}; //
Total_length <= Data_length + 16'd20;
Txd_o <= 8'hZZ;
end
Cal_Head_Crc: begin
if(Send_count == 16'd0) begin
Crc_Sum_Head_r <= Header_Code[223:192] + Header_Code[191:160] + Header_Code[159:128]
+ Header_Code[127:96] + Header_Code[ 95: 64] ;
end
else if(Send_count == 16'd1) begin
Crc_Sum_Head <= Crc_Sum_Head_r[31:16] + Crc_Sum_Head_r[15:0];
end
else if(Send_count == 16'd2) begin
Header_Code[143:128] <= Crc_Sum_Head;
end
else begin
Crc_Sum_Head_r <= Crc_Sum_Head_r;
Crc_Sum_Head <= Crc_Sum_Head;
Header_Code <= Header_Code;
end
end
Write_Leading: begin
Txd_o <= Leading_Code[175:168];
Leading_Code <= {Leading_Code[167: 0],Leading_Code[175:168]};
end
Write_Header: begin
Txd_o <= Header_Code[223:216];
Header_Code <= {Header_Code[215: 0],Header_Code[223:216]};
end
Write_Data: begin
Txd_o <= Fifo_Data_o;
end
Write_Crc: begin
if(Send_count == 16'd0) begin
Txd_o <= {~Crc_Sum_Rec[24],~Crc_Sum_Rec[25],~Crc_Sum_Rec[26],~Crc_Sum_Rec[27],
~Crc_Sum_Rec[28],~Crc_Sum_Rec[29],~Crc_Sum_Rec[30],~Crc_Sum_Rec[31]};
end
else if(Send_count == 16'd1)begin
Txd_o <= {~Crc_Sum_Rec[16],~Crc_Sum_Rec[17],~Crc_Sum_Rec[18],~Crc_Sum_Rec[19],
~Crc_Sum_Rec[20],~Crc_Sum_Rec[21],~Crc_Sum_Rec[22],~Crc_Sum_Rec[23]};
end
else if(Send_count == 16'd2)begin
Txd_o <= {~Crc_Sum_Rec[ 8],~Crc_Sum_Rec[ 9],~Crc_Sum_Rec[10],~Crc_Sum_Rec[11],
~Crc_Sum_Rec[12],~Crc_Sum_Rec[13],~Crc_Sum_Rec[14],~Crc_Sum_Rec[15]};
end
else if(Send_count == 16'd3)begin
Txd_o <= {~Crc_Sum_Rec[ 7],~Crc_Sum_Rec[ 6],~Crc_Sum_Rec[ 5],~Crc_Sum_Rec[ 4],
~Crc_Sum_Rec[ 3],~Crc_Sum_Rec[ 2],~Crc_Sum_Rec[ 1],~Crc_Sum_Rec[ 0]};
end
else Txd_o <= 8'hZZ;
end
Write_Over: begin
Txd_o <= 8'hZZ;
end
因为本模块是持续写入,不中断,所以状态跳转切换就比较简单,单纯的计数就可以。每个状态就是把该发的数据发送出去
达到计数值得时候同步拉高State_turn 状态跳转,实现状态切换。
idle: begin
Send_count <= 16'd0;
State_turn <= 1'b0;
end
Cal_Head_Crc: begin
if (Send_count == 16'd2) begin
Send_count <=16'd0;
State_turn <= 1'b1;
end
else begin
Send_count <= Send_count + 1'b1;
State_turn <= 1'b0;
end
end
Write_Leading: begin
if (Send_count == 16'd21) begin
Send_count <= 16'd0;
State_turn <= 1'b1;
end
else begin
Send_count <= Send_count + 1'b1;
State_turn <= 1'b0;
end
end
Write_Header: begin
if (Send_count == 16'd27) begin
Send_count <=16'd0;
State_turn <= 1'b1;
end
else begin
Send_count <= Send_count + 1'b1;
State_turn <= 1'b0;
end
end
Write_Data: begin
if (Send_count == Data_length - 1'b1) begin
Send_count <= 16'd0;
State_turn <= 1'b1;
end
else begin
Send_count <= Send_count + 1'b1;
State_turn <= 1'b0;
end
end
Write_Crc : begin
if (Send_count == 16'd3) begin
Send_count <=16'd0;
State_turn <= 1'b1;
end
else begin
Send_count <= Send_count + 1'b1;
State_turn <= 1'b0;
end
end
Write_Over: begin
Send_count <= Send_count;
State_turn <= 1'b0;
end
Txen_o信号的维护:在写入数据的状态拉高就可以了
idle,Cal_Head_Crc,Write_Over: begin
Txen_o <= 1'b0;
end
Write_Header,Write_Data,Write_Leading,Write_Crc: begin
Txen_o <= 1'b1;
end
以上,就简单实现了UDP数据包的连续发送。
仿真:画个波形看看情况,代码如下
module test(
output reg clk_o,
output reg rst_n,
output reg [ 3:0] Eth_Command,
output reg [15:0] Data_length,
input Eth_Write_Busy,
output reg [ 7:0] Fifo_Data_i,
input Fifo_en
);
initial begin
clk_o = 1'b1;
forever begin
#10ns clk_o = ~clk_o; //10Mhz
end
end
initial begin
rst_n = 1'b0;
#1000ns rst_n = 1'b1;
end
initial begin
Eth_Command = 4'hf;
Data_length = 16'd1500;
#1500ns
if(Eth_Write_Busy) Eth_Command = 4'hf;
else begin
#1000ns
Eth_Command = 4'hA;
end
end
always @(posedge clk_o or negedge rst_n)
begin
if(!rst_n) begin
Fifo_Data_i <= 8'hFF;
end
else if (Fifo_en)begin
Fifo_Data_i <= Fifo_Data_i + 1'b1;
end
else begin
Fifo_Data_i <= Fifo_Data_i;
end
end
endmodule
仿真截图:
还有没有上电路板验证,秀一下刚画完的板子