Menu Close

IP层接收Verilog工程(eth_ip_rx.v)

IP层接收工程在网络综合实验架构中的位置如图1所示。

ethernet verilog code flowchart

图1

当MAC层接收到数据后,将处理过的数据存储到ip_rx_dram 双端口 RAM IP核, IP 接收层将读取 这个双端口 RAM IP核, 解析相关的IP协议,并送给IP的上层应用,如图2所示。

 

ip rx interface

图2

本模块主要有两个功能需要实现:检查IP包包头信息和将检查后的IP包写入乒乓Buffer。

需要检查IP头中的信息如下所示:

  • 以太网类型 : 0x0800
  • IP 头类型: 0x45
  • 是否是UDP 协议 : 0x11(17)
  • 源IP 地址是否正确
  • 目的IP地址是否正确
  • IP 头的checksum 检查是否正确

当检查过的以太网包都没有问题,将相应的数据写入到UDP_RX_DRAM,整个MAC包都会被写入到UDP_RX_DRAM中。 UDP 层将从MAC包中挑出UDP 的部分,在UDP rx 模块中进行解析。这样做主要是为了方便计算UDP 的checksum 部分。

 

主状态机ip_rx_st逻辑的描述如下,状态示意图如图3(注意省略了状态转移条件不满足时,停留在当前状态的箭头)所示:

状态0: 初始化相关的变量, 将IP_RX_DRAM 和UDP_RX_DRAM的 子地址指针都指向 1023的位置(16bit 地址) ,跳转到状态1。

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

状态2: 如果IP_RX_DRAM 中有数据包,同时UDP_RX_DRAM的buffer 为空,将ip_rx_addrb_sub = 0,跳转到状态3。

状态3: 读取IP_RX_DRAM中的MAC包长度,将ip_rx_addrb_sub = 1,跳转到状态4。

状态4: 写入MAC的34bytes 的数据,跳转到状态5。

状态5: 一直写入数据,直到MAC包中的数据完全写入成功,跳转到状态6。

状态6: 如果检查IP 包中发现错误,UDP_RX_DRAM的buffer 不会封口;如果没有错误,UDP_RX_DRAM的buffer 封口,同时写入包的程度,跳转到状态7。

状态7: 如果检查IP 包中发现错误,跳转到状态14; 如果检查IP 包中没有错误,跳转到状态15。

状态14: 错误处理,IP_RX_DRAM的buffer 指向下一个包的位置,跳转到状态0。

状态15: 正确处理,IP_RX_DRAM的buffer 指向下一个包的位置,UDP_RX_DRAM的buffer指向下一个包的位置,跳转到状态0。

ip rx state machine flowchart

图3 ip_rx_st 示意图

 

`timescale 1ns / 1ps



module eth_ip_rx #
(
    parameter [31:0] RX_IP_SRC     = 32'hc0a8003d,
    parameter RX_IP_SRC_EN  = 0,
    parameter [31:0] RX_IP_DES     = 32'hc0a800ff,
    parameter RX_IP_DES_EN  = 0,

    parameter IP_BUF_SIZE = 10,
    parameter IP_IDX_BIT  = 1
)
(
    input        sys_clk,
    
    input        udp_rx_web,
    input  [IP_BUF_SIZE + IP_IDX_BIT - 1:0] udp_rx_addrb,
    output [15:0] udp_rx_doutb,
    
    output reg  ip_rx_web = 0,
    output [IP_BUF_SIZE + IP_IDX_BIT - 1:0] ip_rx_addrb,
    input  [15:0] ip_rx_doutb,

    output reg [47:0] rx_des_mac = 0,
    output reg [47:0] rx_src_mac = 0,
    output reg [31:0] rx_src_ip = 0,
    output reg [31:0] rx_des_ip = 0,

    output reg [7:0]  rx_ip_err = 0,

    input        reset
);
//=========================================================================
localparam CHECK_ETH_TYPE = 16'h0800;
localparam CHECK_IP_VER   = 8'h45;
localparam CHECK_IP_TYPE  = 8'h11;     // UDP

//=========================================================================
reg  [IP_IDX_BIT - 1:0] ip_rx_idb = 0;
reg  [IP_BUF_SIZE - 1:0] ip_rx_addrb_sub = 0;
assign ip_rx_addrb = {ip_rx_idb, ip_rx_addrb_sub};
//=========================================================================
reg  udp_rx_wea = 0;
reg  [IP_BUF_SIZE - 1:0] udp_rx_addra_sub = 0;
reg  [IP_IDX_BIT - 1:0] udp_rx_ida = 0;
wire [IP_BUF_SIZE + IP_IDX_BIT - 1:0] udp_rx_addra = {udp_rx_ida, udp_rx_addra_sub};

reg  [15:0] udp_rx_dina = 0;
wire [15:0] udp_rx_douta;
//=======================================================================   
reg ip_start = 0;
reg rx_ip_end = 0;

reg  [15:0] ip_pkg_len = 0;
reg  [15:0] ip_rx_len = 0;
reg  [15:0] ip_rx_cnt = 0;


reg  [3:0]  ip_rx_st = 0;
always @ (posedge sys_clk)
if(reset)
begin
    udp_rx_ida <= 0;
    udp_rx_wea <= 0;
    
    ip_rx_idb <= 0;
    ip_rx_web <= 0;
    
    ip_rx_st <= 0;
end 
else case (ip_rx_st)
0:
begin
    rx_ip_end <= 0;
    ip_start <= 0;
    ip_rx_cnt <= 0;
    
    ip_rx_web <= 0;
    ip_rx_addrb_sub <= {IP_BUF_SIZE{1'b1}};
    
    udp_rx_wea <= 0;
    udp_rx_addra_sub <= {IP_BUF_SIZE{1'b1}};
    
    ip_rx_st <= 1;
end
1:
begin
    ip_rx_web <= 0;
    ip_rx_addrb_sub <= {IP_BUF_SIZE{1'b1}};

    udp_rx_wea <= 0;
    udp_rx_addra_sub <= {IP_BUF_SIZE{1'b1}};

    ip_rx_st <= 2;
end
2:
begin
    if( (ip_rx_doutb[15] == 1) && (udp_rx_douta[15] == 0) )
    begin
        ip_rx_addrb_sub <= 0;
        ip_rx_st <= 3;
    end
end
3:
begin
    ip_rx_len <= ip_rx_doutb[11:0];
    ip_rx_addrb_sub <= 1;
    
    ip_start <= 1;       // start to calculate checksum
    ip_rx_st <= 4;
end
4:
begin
    if(ip_rx_cnt >= ip_rx_len)
    begin
        udp_rx_wea <= 0;
        ip_rx_st <= 6;
    end
    else       //   read data from ip buffer, start from address 0
    begin
        ip_rx_cnt <= ip_rx_cnt + 2;
        ip_rx_addrb_sub <= ip_rx_addrb_sub + 1;

        udp_rx_dina <= ip_rx_doutb;
        udp_rx_wea <= 1;
        udp_rx_addra_sub <= udp_rx_addra_sub + 1;
        
        if(ip_rx_cnt == 32) // 6 + 6 + 2 + 20 = 34
        begin
            ip_rx_st <= 5;
        end
    end
end
5:
begin
    if(ip_rx_cnt >= ip_rx_len)
    begin
        udp_rx_wea <= 0;
        ip_rx_st <= 6;
    end
    else      // continue 
    begin
        ip_rx_cnt <= ip_rx_cnt + 2;
        ip_rx_addrb_sub <= ip_rx_addrb_sub + 1;
        
        udp_rx_dina <= ip_rx_doutb;
        udp_rx_wea <= 1;
        udp_rx_addra_sub <= udp_rx_addra_sub + 1;
    end
end
6:
begin
    if(|rx_ip_err)
        udp_rx_dina <= 0;
    else    
        udp_rx_dina <= {4'b1000, ip_pkg_len[11:0]};
    
    udp_rx_addra_sub <=  {IP_BUF_SIZE{1'b1}};
    udp_rx_wea <= 1;
    
    ip_rx_addrb_sub <=  {IP_BUF_SIZE{1'b1}};
    ip_rx_web <= 1;
    
    ip_rx_st <= 7;
end
7:
begin
    udp_rx_wea <= 0;
    ip_rx_web <= 0;
    if(|rx_ip_err)
        ip_rx_st <= 14;
    else
        ip_rx_st <= 15;
end

14:
begin
    ip_rx_idb <= ip_rx_idb + 1;   //  write abother packet to udp buffer
    
    udp_rx_wea <= 0;
    ip_rx_web <= 0;

    rx_ip_end <= 1;  //  stop calculating checksum, clear error
    ip_rx_st <= 0;
end
15: // normal finished
begin
    udp_rx_ida <= udp_rx_ida + 1; // switch to another udp buffer
    ip_rx_idb <= ip_rx_idb + 1;
    
    udp_rx_wea <= 0;
    ip_rx_web <= 0;

    rx_ip_end <= 1;
    ip_rx_st <= 0;
end
default: ip_rx_st <= 0;
endcase

//=======================================================================   

reg [15:0] rx_eth_type = 0;
reg [7:0]  rx_ip_type = 0;
reg [7:0]  rx_ip_header = 0;
reg [23:0] rx_checksum = 0;


wire [15:0] ip_data = {ip_rx_doutb[7:0], ip_rx_doutb[15:8]};
reg [3:0] ip_rx_chk_st = 0;
always @ (posedge sys_clk)
if(!ip_start) 
begin
    rx_ip_err <= 0;
    ip_rx_chk_st <= 0;
end
else case (ip_rx_chk_st)
0:
begin
    rx_ip_err <= 0;
    rx_des_mac <= {rx_des_mac[31:0], ip_data};

    if(ip_rx_cnt == 4) // check destination MAC address
        ip_rx_chk_st <= 1;
end 
1:
begin
    rx_src_mac <= {rx_src_mac[31:0], ip_data};
    
    if(ip_rx_len < 34 ) 
    begin
        rx_ip_err[6] <= 1; // receive package length error
        ip_rx_chk_st <= 14;
    end
    else if(ip_rx_cnt == 10) // check source MAC address
        ip_rx_chk_st <= 2;
end 
2:
begin
    rx_eth_type <= ip_data;
    rx_checksum <= 0;

    ip_rx_chk_st <= 3;
end 
3:
begin
    rx_ip_header <= ip_data[15:8];
    rx_checksum <= rx_checksum + ip_data;

    if(rx_eth_type == CHECK_ETH_TYPE) //16'h0800
        ip_rx_chk_st <= 4;  // ethernet type = 0800 OK
    else
    begin
        rx_ip_err[0] <= 1;
        ip_rx_chk_st <= 14; // ethernet type = 0800 error
    end
end 
4:
begin
    ip_pkg_len <= ip_data;
    rx_checksum <= rx_checksum + ip_data;
    
    if(rx_ip_header == CHECK_IP_VER) // 8'h45
        ip_rx_chk_st <= 5;  // ip type = 45  OK
    else
    begin
        rx_ip_err[1] <= 1;
        ip_rx_chk_st <= 14; // ip type = 45  error
    end
end 
5:
begin
    rx_checksum <= rx_checksum + ip_data;
    
    if(ip_rx_cnt == 20)  // do not check
    begin
        ip_pkg_len <= ip_pkg_len + 14;
        ip_rx_chk_st <= 6;
    end
end 
6:
begin
    rx_checksum <= rx_checksum + ip_data;
    rx_ip_type <= ip_data[7:0];
    ip_rx_chk_st <= 7;
end 
7:
begin
    rx_checksum <= rx_checksum + ip_data;

    if(rx_ip_type  == CHECK_IP_TYPE) // 11 == UDP
        ip_rx_chk_st <= 8;  // udp package ok
    else
    begin
        rx_ip_err[2] <= 1;
        ip_rx_chk_st <= 14; // udp package error
    end
end 
8:
begin
    rx_checksum <= rx_checksum + ip_data;
    rx_src_ip <= {rx_src_ip[15:0], ip_data};
    if(ip_rx_cnt == 28)  // get ip package length
        ip_rx_chk_st <= 9;
end 
9:
begin
    rx_checksum <= rx_checksum + ip_data;
    rx_des_ip <= {rx_des_ip[15:0], ip_data};

    if(ip_rx_cnt == 32)  // get ip package length
        ip_rx_chk_st <= 10;

    if( (RX_IP_SRC_EN == 1) && (rx_src_ip != RX_IP_SRC) )
    begin
        rx_ip_err[3] <= 1;  // src ip error
        ip_rx_chk_st <= 14; // udp package error
    end
end 
10:
begin
    if(|rx_checksum[23:16])
        rx_checksum <= rx_checksum[15:0] + rx_checksum[23:16];
    else
    begin
        rx_checksum <= ~rx_checksum;
        ip_rx_chk_st <= 11;
    end
    
    if( (RX_IP_DES_EN == 1) && (rx_des_ip != RX_IP_DES) )
    begin
        rx_ip_err[4] <= 1;  // des ip error
        ip_rx_chk_st <= 14; // udp package error
    end
end
11:
begin
    if(rx_checksum[15:0] == 0) ip_rx_chk_st <= 15; // ip header checksum ok
    else 
    begin
        rx_ip_err[7] <= 1;
        ip_rx_chk_st <= 14; // ip header checksum error
    end
end



14: // ip check error
begin
    ip_rx_chk_st <= 15;
end
15: // normal ip finished
begin
    if(rx_ip_end)
    begin
        rx_ip_err <= 0;
        ip_rx_chk_st <= 0;
    end
end
default: ip_rx_chk_st <= 0;
endcase

//=========================================================================
UDP_DRAM   UDP_RX_DRAM_inst
(
    .clka   (sys_clk),      // input wire clka
    .wea    (udp_rx_wea),   // input wire [0 : 0] wea
    .addra  (udp_rx_addra), // input wire [10 : 0] addra
    .dina   (udp_rx_dina),  // input wire [15 : 0] dina
    .douta  (udp_rx_douta), // output wire [15 : 0] douta
    
    .clkb   (sys_clk),      // input wire clkb
    .web    (udp_rx_web),   // input wire [0 : 0] web
    .addrb  (udp_rx_addrb), // input wire [10 : 0] addrb
    .dinb   (0),            // input wire [15 : 0] dinb
    .doutb  (udp_rx_doutb)  // output wire [15 : 0] doutb
);

//=========================================================================
endmodule

 

伴随状态机(ip_rx_chk_st)逻辑的描述如下,状态示意图如图4(注意省略了状态转移条件不满足时,停留在当前状态的箭头)所示:

在主状态机上,析出相关的数据, 中间任何一步检查到错误,都会跳转到状态14:

状态0:析出des MAC地址, 跳转到状态1。

状态1:析出src MAC地址, 跳转到状态2。

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

状态3:析出 以太网类型 是否是0x0800,是则跳转到状态4,否则跳转到状态14。

状态4:析出IP 头类型是否是0x45,是则跳转到状态5,否则跳转到状态14。

状态5:计算到 第20 byte 的checksum,跳转到状态6。

状态6:读出IP协议类型,跳转到状态7。

状态7: 计算checksum。析出协议类型是否是0x11(UDP),是则跳转到状态8,否则跳转到状态14。

状态8:析出源IP地址, 计算checksum,跳转到状态9。

状态9: 计算checksum。判断源IP地址,如果错误,则跳转到状态14。否则析出目的IP地址,跳转到状态10。

状态10:计算checksum结果。判断目的IP地址,如果错误,则跳转到状态14。否则跳转到状态11。

状态11:如果checksum结果正确,跳转到状态15; 如果checksum 结果错误,跳转到状态14。

状态14:等待一个时钟周期,跳转到状态15。

状态15:等待rx_ip_end信号,跳转到状态0。

 

图4 ip_rx_chk_st示意图

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

发表回复

相关链接