之前写好了UDP ,IP ARP,MAC,ETHER层的各个发送和接收,今天时间有限之做了一件事,就是将发射部分拼装起来了。
方框图如下:
?这里简单说说我的思路,按照实际场景来说明:
0,如果我们的MAC接收到一个发给我们IP的ARP_REQUEST包,则要做两件事A,保存目标IP地址和物理地址到ARP_CACHE(这样我们发给他IP包时候就不需要询问他的物理地址了),B,返回一个ARP_REQPLY让对方知道我们的物理地址。这里说的物理地址就是48位MAC地址。
1,上述这条实际是接收部分实现,此模块留了一个接口就是上方的UPDATE_IP_MAC。可以说ARP_CACHE条目的更新就是通过接收部分解析ARP_REQUEST包和ARP_REPLY包来实现的。
2,我们要发送一个UDP数据包,传递给这个模块UDP报文数据流以及目标IP和目标端口以及自己的端口信息。
3,UDP信息进入UDP_IP模块,以此组装成UDP包,以及IP包,成为可以发送给MAC层的数据流。
4,MAC发送使用的是物流地址也就是48位的MAC地址,此模块内部设置状态机自动询问ARP_CACHE找出来对应的MAC地址。
5,如果没有找到,则通过给给ARP_GEN模块设置一写输入生成一个ARP_REQUEST的数据包,之后通过多路器传递给MAC_TX。
6,网络中的主机收到ARP_REQUEST后返回ARP_REPLY包汇报自己地址的。我们接收部分收到ARP_REPLY包后解析出来IP和MAC地址,通过上述框图的上方UPDATE_IP_UDP接口来传递给ARP_CACHE。
7,图中看见三个发送部分(其中最下面部分相应PING命令的ICMP_REPLY还没有写好),这个模块内部的状态机就是轮询这几个输入,根据情况控制多路器输出给MAC_TX进行发送。是不断轮询工作的。
8,这次轮询发现要发送的IP没有对应的物流地址,就发送了ARP_REQUEST数据包来请求物理地址,之后继续轮询。在若干毫秒后收到了ARP_REPLY,更新了ARP_CACHE。再次轮询的时候找到了物流地址,此时就会把这个滞留的IP包发出去。
9,在ARP_REQUEST发送到时候ARP_REPLY并更新ARP_CACHE期间这段时间会轮询很多次UDP_IP这路发射通道,为了防止每轮询以此就发送一个ARP_REQUEST,我这里设置了两个寄存器一个存储询问的IP地址一个计算时间,保证下次请求同一个IP地址的ARP_REQUEST数据包在一秒后发出。
10,这里我们能看到如果网络无法找到某个IP,则这个UDP会一直滞留没有做出错处理,这个可以先一放,如果实际应用需要处理这个问题了,可以再根据实际情况处理一下。由于采用状态机主控,寄存器以及模块之间的耦合在时序上比较松散灵活。
今天时间不够,写好代码了,编译检查过了,但是没有进行仿真。先上代码:
/*
mac_hub mac_hub (
.clk( ),
.rst( ),
.cfg_my_ip( ),
.cfg_my_mac( ), // local configuration
.s_udp_ip( ),
.s_udp_src_port( ) ,
.s_udp_dst_port( ) ,
.s_udp_tx_valid( ) ,
.s_udp_tx_busy ( ),
.s_udp_tx_len( ) ,
.s_udp_tx_dat( ), // udp port
.s_udp_ip_id( ),
.s_udp_tx_start( ) , // id in IP layer, may a const
.arp_reply_dst_ip ( ),
.arp_reply_dst_mac( ) ,
.arp_reply_valid( ) ,
.arp_reply_ready( ),// to send a arp_reply package
.phy_tx_en ( ),
.phy_tx_err ( ),
.phy_tx_data( ) , // gmii if
.arp_cache_mac_in ( ) ,
.arp_cache_ip_in( ) ,
.arp_cache_wr_en( ) // update arp cache from arp
);
*/
module mac_hub (
input clk,rst,
input [31:0] cfg_my_ip,
input [47:0] cfg_my_mac, // local configuration
input [31:0]s_udp_ip,
input[15:0] s_udp_src_port ,s_udp_dst_port ,
input s_udp_tx_valid ,
output s_udp_tx_busy ,
input [15:0] s_udp_tx_len ,
input [7:0] s_udp_tx_dat, // udp port
input [15:0] s_udp_ip_id,
input s_udp_tx_start , // id in IP layer, may a const
input [31:0] arp_reply_dst_ip ,
input [47:0] arp_reply_dst_mac ,
input arp_reply_valid ,
output reg arp_reply_ready,// to send a arp_reply package
output phy_tx_en , phy_tx_err ,
output [7:0] phy_tx_data , // gmii if
input [47:0] arp_cache_mac_in ,
input [31:0] arp_cache_ip_in ,
input arp_cache_wr_en // update arp cache from arp
);
reg [47:0] arp_dst_mac ;
reg [31:0] arp_dst_ip ;
reg arp_req0_rep1 , arp_s_valid ;
wire arp_s_ready ;
reg arp_load ;
arp_gen arp_gen (
.clk (clk ),
.rst(rst ) ,
.my_ip( cfg_my_ip ),
.my_mac( cfg_my_mac ),
.dst_ip( arp_dst_ip ),
.dst_mac( arp_dst_mac),
.req0_rep1(arp_req0_rep1 ) ,// request=0 ,reply=1
.arp_ack ( mac_s_ack ) ,
.arp_load ( arp_load ) ,
.arp_data ( arp_data ) ,
.arp_last ( arp_last )
);
always @(posedge clk )if (rst)arp_load<=0; else case ( st ) (100+1) ,(300+1) : arp_load <=1;default arp_load<=0;endcase
always @(posedge clk )if (rst) {arp_dst_mac,arp_dst_ip} <=0; else case ( st ) 100 : {arp_dst_mac,arp_dst_ip} <= { arp_reply_dst_mac , arp_reply_dst_ip };300:{arp_dst_mac,arp_dst_ip} <= {48'b0,s_udp_ip} ;endcase
always @(posedge clk )if (rst)arp_req0_rep1<=0; else case (st)100:arp_req0_rep1<=1;300:arp_req0_rep1<=0;endcase
/*
*/
reg [31:0] arp_cache_ip_req ;
wire [47:0] arp_cache_mac_out ;
wire arp_cache_req_done , arp_cache_req_hit ;
reg arp_cache_req_en ;
arp_cache arp_cache(
.clk (clk ),
.rst (rst ),
.ip_in( arp_cache_ip_in ),
.mac_in( arp_cache_mac_in),
.wr_en( arp_cache_wr_en ),
.wr_busy ( ),
.req_en (arp_cache_req_en),
.ip_req( arp_cache_ip_req ),
.mac_out( arp_cache_mac_out ),
.req_done(arp_cache_req_done ),
.req_hit( arp_cache_req_hit)
);
wire [7:0] udp_m_ip_tx_data ;
udp_ip_pack udp_ip_pack (
.clk(clk ),
.rst(rst ),
.enable( udp_ip_enable ),
.s_src_ip(cfg_my_ip ),
.s_dst_ip(s_udp_ip ),
.s_src_port(s_udp_src_port ),
.s_dst_port(s_udp_dst_port ),
.s_tx_len ( s_udp_tx_len ),
.s_tx_start ( s_udp_tx_start ),
.s_tx_busy ( s_udp_tx_busy ),
.s_tx_dat (s_udp_tx_dat ),
.s_ip_id (s_udp_ip_id ), // from upper layer
.m_ip_pack_valid (udp_m_ip_pack_valid),
.m_ip_tx_last(m_ip_tx_last ),
.m_ip_tx_ack (mac_s_ack ) ,
.m_ip_tx_data( m_ip_tx_data) // to mac layer
);
reg [47:0] mac_tx_dst_mac; always @ (posedge clk ) mac_tx_dst_mac <= arp_cache_mac_out ;
//reg [47:0] mac_tx_dst_mac; always @ (posedge clk )if (st ==220 ) mac_tx_dst_mac <= arp_cache_mac_out ;
reg [7:0]mac_s_ip_tx_data ;
localparam MAC_TYPE_IP = 'H3421;
localparam MAC_TYPE_ARP = 'H5678;
reg [15:0] mac_type = 0 ; always @ (posedge clk) case (st) 100,300:mac_type <= MAC_TYPE_ARP ; 200:mac_type <= MAC_TYPE_IP ; endcase
reg mac_s_valid ;
always @ (posedge clk) case (st) 103, 222,310 :mac_s_valid <= 1 ; default mac_s_valid <= 0 ; endcase
reg mac_s_last;
mac_tx mac_tx (
.clk(clk ) ,
.rst(rst ) ,
.s_valid( mac_s_valid ) ,
.s_last( mac_s_last ),
.s_busy(mac_s_busy ) ,
.s_ack(mac_s_ack ) ,
.s_ip_tx_data( mac_s_ip_tx_data) ,
.s_src_mac( cfg_my_mac ),
.s_dst_mac( mac_tx_dst_mac ),
.s_type( mac_type ), //
.phy_tx_en( phy_tx_en ),
.phy_tx_err( phy_tx_err ),
.phy_tx_data( phy_tx_data )
);
always @ (posedge clk ) arp_reply_ready <= st==112 ;
reg [31:0] sending_arp_ip ;
reg [31:0] sending_time_cntr ;
always @(posedge clk) arp_cache_ip_req <= s_udp_ip ;
localparam ARP_TIMEOUT = 1000*1000*125 ;
always @(posedge clk) if (rst )sending_arp_ip <= 0 ; else case (st) 312: sending_arp_ip<=arp_dst_ip ;endcase
always @(posedge clk) if (rst )sending_time_cntr<= (1000*1000*125) ;
else if (st == 312) sending_time_cntr <= 32'h0;else if (ARP_TIMEOUT!=sending_time_cntr)
sending_time_cntr<=sending_time_cntr+1;
wire arp_in_sending = (s_udp_ip == sending_arp_ip ) && ( sending_time_cntr != ARP_TIMEOUT ) ;
reg[7:0]st ; always@(posedge clk)if (rst)st<=0;else case (st)
0:st<=10;
10 : if (arp_reply_valid)st<=100;else st<=200; //鍙戦佷竴涓猘rp璇锋眰鍖
100,101,102,103:st<=st+1;//浣跨敤4涓懆鏈熷畬鎴愬彂閫佽缃
103: if (mac_s_busy==0)st<=111;
111: if (mac_s_busy==1)st<=112;
112: if (mac_s_busy==0)st<=113;
113: st<=200;
200: if (udp_m_ip_pack_valid==0) st<=400;else st<=210;
210: if (arp_cache_req_done) st<=(arp_cache_req_hit)?220:300;// with IP find out MAC //
220: st<=221; //mac_tx_dst_mac <= arp_cache_mac_out ;
221: st<=222; //s_udp_tx_start=1 in this state otherwise will be 0
222: if (mac_s_busy==0)st<=223;
223: if (mac_s_busy==1)st<=224;
224: if (mac_s_busy==0)st<=225;
// 3state prevous while connect upd_ip to mac_tx
225: st<=300;
300: st<=301;//we need send a arp request
301: st <= (arp_in_sending) ? 313:302;
302,303,304,305,306,307,308,309:st<=st+1;
310: if (mac_s_busy==0)st<=311;
311: if (mac_s_busy==1)st<=312;
312: if (mac_s_busy==0)st<=313;//record sending ip
313: st<=314;
314: st<=10;
default st<=0;
endcase
always @* case (st)
100,101,102,103,111,112,200 , 300,301,302,303,304,305,306,307,308,309,310,311,312: begin mac_s_last <= arp_last ;mac_s_ip_tx_data <= arp_data;end
220,221,22,223,224 : begin mac_s_last <= m_ip_tx_last ;mac_s_ip_tx_data <= m_ip_tx_data;end
default begin mac_s_last <= 0 ;mac_s_ip_tx_data <= 0;end
endcase
endmodule
此模块暂时不进行仿真,明天看有时间再组装起来下图的模块,其中红框部分就是上述代码mac_hub,这样就整个设计彻底封装起来了(虽然暂时还没有实现ICMP回应),下一步就可以使用两个这样的模块(模拟两套以太网板子)进行UDP收发测试。
上图中MAC_RX传递给MAC_HUB的内容包含三部分 ARP_REPLY的发送,ICMP_REPLY的发送,以及ARP包解析出来的IP和物理地址的对应。
希望明天时间能够一些,可以组装在一起并完成两个板子互通的仿真。如果完成的话后天就可以上板子运行了。
上板子运行时候,先实现一下MAC层发送,用WIRESHARK查看一下接收,确保发送底层实现OK。再用PING命令PING咱们板子,虽然没有实现PING回复但是可以让我们电脑网卡发出ARP_REQUEST命令,我们在FPGA内部嵌入上逻辑分析仪,对比看到接收到序列完全正确就可以确保接受底层部分OK。
收发底层OK后剩下纯粹的RTL算法的成功是么有任何悬念:首先我们用的是同步时钟设计思路并且单时钟,其次在编写RTL时候可以思考了寄存器尽量少的扇出和尽量短的逻辑电路长度(尽量少的LUT串联级数),确保能跑到很高足够时钟频率。
下图是QUARTUS来看RTL连接,这样能直观看各线路的连接。
?下图是MAC_HUB部分的层次结构,另外也可以看到实际使用了很少的LOGIC ELEMENT和BLACK RAM 。
这里看到存储使用数量跟我们的定义的12位8BIT的内存数量不一致,应该是2048*8位,而我们看到只报出了使用4096位,这需要研究明白原因。
{{aAxvOXMOIvVUoXMxvoxiowMwWV8xxWTxoxOIOVIUUOvwVOUiIoUvvTMMVMwovWHWX8vOUOUVUwowVIoWwOMIViUIioOIWxmmMMHwMHHMiOWMmwUxiOMoiHIoVU8VvmvIWXTvvOvv8xvMovOWoVmmxoMM8UmooOTvvwUIoTwvmWUoiTw8VmvoHWwMIUWOixiowiUoiXwiwwMMIiIXHwUmOWUVmXXwV8iHWOTUiwTU8xwOoV8HVmTWZz}}
|