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

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

图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。

图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示意图