Menu Close

以太网UDP 实验(eth_udp_tx.v)

当从应用层得到数据时, eth_udp_tx.v 模块将在应用层发送过来数据的基础上,附加上udp 源port, udp 目的port , udp 长度, udp 的checksum 信息。 打成一个完整的udp包,送给ip层。在这个模块中,UDP_TX_CHECKSUM_EN 参数用来控制是否在udp 数据上加载checksum信息,如果不加载checksum 信息, 在checksum 的位置上填写0; 如果加载checksum,就需要计算 udp 头和整体的udp 数据,生成真实的checksum。

ethernet verilog code flow chart

图1

ethernet UDP interface

图2

本节内容关心的一些关键信号,以时序图的方式呈现,如图3所示:

图3

 

udp_tx_st和udp_chk_st两个状态机的示意如图4(注意省略了状态转移条件不满足时,停留在当前状态的箭头)所示。

图4 udp_tx_st和udp_chk_st状态机示意图

 

udp_tx_st 状态机逻辑:

状态机0:初始化相关的数据,UDP_TX_DRAM的子地址指向 1023,跳转到状态机1。

状态机1:等待1个时钟周期,跳转到状态机2。

状态机2:当buffer 目前为空,跳转到状态机3。

状态机3:在地址0 的位置上填写udp 源port,跳转到状态机4。

状态机4:在地址1 的位置上填写udp 目的port,跳转到状态机5。

状态机5:先是在地址3 的位置上填写udp checksum 为 0,跳转到状态机6。

状态机6:将应用层的数据(byte)写入到buffer中去,跳转到状态机7。

状态机7:将应用层的数据(byte)写入到buffer中去,跳转到状态机8,如果没有数据,跳转到状态9。

状态机8:将应用层的数据(byte)写入到buffer中去,跳转到状态机7,如果没有数据,跳转到状态9。

状态机9:正常的数据包结束,在地址2 的位置上填写udp 的长度,如果需要计算checksum,跳转到状态10; 如果不需要checksum, 跳转到状态13。

状态机10:在udp头上填写udp 的checksum,跳转到状态13。

状态机13:对UDP_TX_DRAM的buffer进行封包,跳转到状态14。

状态机14:跳转到状态15。

状态机15:UDP_TX_DRAM的buffer 加一,跳转到状态0。

 

udp_chk_st 伴随状态机:

状态0: 初始化 udp_chk_sum = 16’h0011, 跳转状态 1。

状态1: 计算udp 数据的checksum, 当udp数据写入结束后,跳转状态 2。

状态2: 再计算一次udp length的checksum,跳转状态 3。

状态3: 计算源ip 地址checksum,跳转状态 4。

状态4: 计算源ip 地址checksum,跳转状态 5。

状态5: 计算目的ip 地址checksum,跳转状态 6。

状态6: 计算目的ip 地址checksum,跳转状态 7。

状态7: 计算完整的udp的checksum,标识udp checksum 计算结束,跳转状态 8。

状态8: 等待当前包封包结束,跳转状态 0。

 

eth_udp_tx.v 代码:

`timescale 1ns / 1ps



module eth_udp_tx #
(
    parameter [31:0] TX_IP_SRC          = 32'hc0a8003d,
    parameter [31:0] TX_IP_DES          = 32'hc0a800ff,

    parameter [15:0] UDP_TX_SRC_PORT    = 16'hffff,
    parameter [15:0] UDP_TX_DES_PORT    = 16'hffff,
    
    parameter UDP_TX_CHECKSUM_EN        = 0,
    
    parameter IP_BUF_SIZE               = 10,
    parameter IP_IDX_BIT                = 1
)
(
    input           sys_clk,

    input  [7:0]    udp_din,
    input           udp_din_vld,
    output reg      udp_din_rdy = 0,
    
    output reg      udp_tx_wea = 0,
    output [IP_BUF_SIZE + IP_IDX_BIT - 1:0] udp_tx_addra,
    output reg  [15:0] udp_tx_dina = 0,
    input  [15:0]   udp_tx_douta,
    
    input           reset
);
//=========================================================================
reg  [IP_BUF_SIZE - 1:0] udp_tx_addra_sub = 0;
reg  [IP_IDX_BIT - 1:0] udp_tx_ida = 0;
assign udp_tx_addra = {udp_tx_ida, udp_tx_addra_sub};
//=======================================================================   
reg  [31:0] udp_chk_sum = 16'h0011;     //UDP protocol
reg  udp_chk_end = 0;
reg  udp_chk_vld = 0;
reg  [15:0] udp_tx_cnt = 0;
reg  [3:0]  udp_tx_st = 0;
always @ (posedge sys_clk)
if(reset)
begin
    udp_din_rdy <= 0;
    udp_tx_ida <= 0;
    udp_tx_wea <= 0;
    
    udp_tx_st <= 0;
end 
else case (udp_tx_st)
0:
begin
    udp_chk_end <= 0;
    udp_tx_cnt <= 8;    //count the UDP header
    
    udp_tx_wea <= 0;
    udp_tx_addra_sub <= {IP_BUF_SIZE{1'b1}};
    
    udp_tx_st <= 1;
end
1:
begin
    udp_tx_wea <= 0;
    udp_tx_addra_sub <= {IP_BUF_SIZE{1'b1}};

    udp_tx_st <= 2;
end
2:
begin
    if(udp_tx_douta[15] == 0)
    begin
        udp_tx_st <= 3;
    end
end
3:
begin
    udp_tx_addra_sub <= udp_tx_addra_sub + 1;     //addr 0
    udp_tx_dina <= UDP_TX_SRC_PORT[15:0];
    udp_tx_wea <= 1;
    udp_tx_st <= 4;
end
4:
begin
    udp_tx_addra_sub <= udp_tx_addra_sub + 1;     //addr 1
    udp_tx_dina <= UDP_TX_DES_PORT[15:0];
    udp_tx_wea <= 1;
    udp_tx_st <= 5;
end
5:
begin
    udp_tx_addra_sub <= 3;  // checksum 
    udp_tx_dina <= 16'h0000;
    udp_tx_wea <= 1;
    udp_din_rdy <= 1;
    udp_tx_st <= 6;
end
6:     //state 6-7 together write 2 bytes, using 2 clocks to fill 16 bits data into A/B Buffer
begin
    udp_tx_wea <= 0;
    if(udp_din_vld & udp_din_rdy)
    begin
        udp_tx_addra_sub <= udp_tx_addra_sub + 1;
        udp_tx_dina <= {udp_din, 8'h00};  //odd

        udp_tx_cnt <= udp_tx_cnt + 1;
        udp_tx_st <= 7;
    end
end
7:    //state 7-8 together write 2 bytes, using 2 clocks to fill 16 bits data into A/B Buffer
begin
    udp_tx_wea <= 1;

    if(udp_din_vld & udp_din_rdy)
    begin
        udp_tx_dina[7:0] <= udp_din;  // even
        udp_tx_cnt <= udp_tx_cnt + 1;
        
        if(udp_tx_cnt == 1471) // 1518 - 4 (crc) - 20 - 14 - 8
            udp_din_rdy <= 0;
        
        udp_tx_st <= 8;
    end
    else
    begin
        udp_din_rdy <= 0;
        udp_tx_st <= 9;
    end
end
8:
begin
    udp_tx_wea <= 0;

    if(udp_din_vld & udp_din_rdy)
    begin
        udp_tx_addra_sub <= udp_tx_addra_sub + 1;
        udp_tx_dina <= {udp_din, 8'h00};

        udp_tx_cnt <= udp_tx_cnt + 1;
        if(udp_tx_cnt == 1471) // 1518 -4 (crc) - 20 - 14 - 8
            udp_din_rdy <= 0;
            
        udp_tx_st <= 7;
    end
    else
    begin
        udp_din_rdy <= 0;
        udp_tx_st <= 9;
    end
end



9:
begin
    udp_tx_addra_sub <= 2;
    udp_tx_dina <= udp_tx_cnt[15:0];
    udp_tx_wea <= 1;
    udp_chk_end <= 1;
    if(UDP_TX_CHECKSUM_EN)
        udp_tx_st <= 10;
    else
        udp_tx_st <= 13;
end
10:
begin
    udp_tx_wea <= 0;
    
    if(udp_chk_vld)
    begin
        udp_tx_addra_sub <= 3; // checksum
        udp_tx_dina <= udp_chk_sum[15:0];
        udp_tx_wea <= 1;
        udp_tx_st <= 13;
    end
end
13:
begin
    udp_tx_addra_sub <= {IP_BUF_SIZE{1'b1}};
    udp_tx_dina <= {4'b1000, udp_tx_cnt[11:0]};
    udp_tx_wea <= 1;
    
    udp_tx_st <= 14;
end
14:
begin
    udp_chk_end <= 0;
    udp_tx_wea <= 0;
    udp_tx_st <= 15;
end

15: // normal finished
begin
    udp_tx_ida <= udp_tx_ida + 1;
    
    udp_tx_wea <= 0;
    udp_tx_st <= 0;
end
default: udp_tx_st <= 0;
endcase


reg [3:0] udp_chk_st = 0;
always @ (posedge sys_clk)
if(reset)     
begin
    udp_chk_vld <= 0;
    udp_chk_st <= 0;
    udp_chk_sum <= 16'h0011;
end
else case (udp_chk_st)
0:
begin
    udp_chk_vld <= 0;
    udp_chk_sum <= 16'h0011;
    udp_chk_st <= 1;
end
1:
begin
    if(udp_tx_wea) udp_chk_sum <= udp_chk_sum + udp_tx_dina;
    
    if(udp_chk_end) udp_chk_st <= 2;
end
2:   //add UDP length again
begin
    udp_chk_sum <= udp_chk_sum + udp_tx_dina;
    udp_chk_st <= 3;
end
3:
begin
    udp_chk_sum <= udp_chk_sum + TX_IP_SRC[31:16];
    udp_chk_st <= 4;
end
4:
begin
    udp_chk_sum <= udp_chk_sum + TX_IP_SRC[15:00];
    udp_chk_st <= 5;
end
5:
begin
    udp_chk_sum <= udp_chk_sum + TX_IP_DES[31:16];
    udp_chk_st <= 6;
end
6:
begin
    udp_chk_sum <= udp_chk_sum + TX_IP_DES[15:00];
    udp_chk_st <= 7;
end
7:
begin
    if(|udp_chk_sum[31:16]) 
        udp_chk_sum <= udp_chk_sum[15:0] + udp_chk_sum[31:16];
    else 
    begin
        udp_chk_sum[15:0] <= ~udp_chk_sum[15:0];
        udp_chk_vld <= 1;
        udp_chk_st <= 8;
    end
end
8:
begin
    udp_chk_vld <= 0;
    if(udp_chk_end == 0)
        udp_chk_st <= 0;
end
default: udp_chk_st <= 0;
endcase
//=========================================================================
    
endmodule

更多详细内容请参考相关视频。

扩展练习:将本模块中的一段式状态机改成三段式状态机实现。

Posted in FPGA, FPGA 教材教案, RISC-V, Verilog, Verilog, 教材与教案, 文章

发表回复

相关链接