以太网MAC帧发送设计:fifo_mac_send

本文详细介绍了以太网的基础知识,特别是MAC层的作用,包括MAC地址的分配和帧封装。重点解析了fifo_mac_send模块的设计,包括数据采集、FIFO管理和MAC帧的发送过程。通过实例代码展示如何通过状态机控制数据发送,并提供了两个MAC帧发送的仿真结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.基础知识

1.网络结构:计算机网络的层次结构如图所示。

2.以太网:以太网包括了数据链路层和物理层,数据链路层可以细分为LLC层和MAC层。

我们需要了解的是MAC层,它有两个作用:

①每一个网卡分配一个唯一的MAC地址(硬件地址),用以区分不同通信设备。

②对上层的数据进行封装,变成MAC帧,通过接口传到物理层,然后再通过物理介质传输到其他设备。

如图所示,mac与物理层的接口有多种:

①MII:支持10M和100Mbps。RMII是MII的简化版。

GMII:支持1000Mbps,即千兆网,RGMII是MII的简化版。

至于接口的细节可以参见小梅哥的教程,日常我们只要知道有这些接口存在即可。

3.ARP帧:

简单地了解:

ARP帧是用来获取目的主机的硬件地址的(即MAC地址),利用已知的目的主机的IP地址和端口号来查找目的MAC地址。

工作时,源主机发出ARP数据包请求并广播,目的主机收到广播后发出一个ARP应答数据包告诉对方它的MAC地址。

ARP和IP是两个相互独立的协议,都属于网络层,ARP更底层,没有ARP提高的映射功能,IP 数据包无法在以太网上发送。

二.以太网MAC帧发送设计:fifo_mac_send

1.建立模型:

2.可以实现的功能:

  通过数据采集模块采集的数据存于FIFO中,当FIFO中的数据量满足要求时,控制端向MAC_send模块发送一个ctr_tx_en脉冲信号,指示该模块开始把FIFO中的数据封装成一个MAC帧发送到PHY芯片上。

其中,目的mac地址dst_mac_add通过ARP帧广播获取,源mac地址在本机上,crc32为校验位。

 3.代码实现:

module fifo_mac_send(
    clk,
    reset,
    data_in,
    write_en,
    dst_mac_add,
    src_mac_add,
    type_length,
    crc32,
    ctrl_tx_en,
    tx_en,//数据发送使能信号
    tx_data//待发送的数据    
    );
    
  input clk ;
  input reset ;
  input [7:0]data_in ;
  input  write_en ;
  input [47:0]dst_mac_add;//目的mac地址
  input [47:0]src_mac_add;//本地mac地址
  input [15:0]type_length;//类型/长度
  input [31:0]crc32;//效验码 
  input ctrl_tx_en; 
  output tx_en ;
  output [3:0]tx_data ;
  wire data_req ;
  wire [7:0]dout ;
  wire full;
  wire empty ;
  wire [10:0]data_count ;
    
   FIFO_ip_or_arp fifo (
  .clk(clk),                // input wire clk
  .srst(reset),              // input wire srst
  .din(data_in),                // input wire [7 : 0] din
  .wr_en(write_en),            // input wire wr_en
  .rd_en(data_req),            // input wire rd_en
  .dout(dout),              // output wire [7 : 0] dout
  .full(full),              // output wire full
  .empty(empty),            // output wire empty
  .data_count(data_count)  // output wire [10 : 0] data_count
);
    
    mac_send    mac_send(
    .tx_clk(clk),//输入时钟
    .reset(reset) ,
    .ctrl_tx_en(ctrl_tx_en),//输入使能端
    .data_in(dout),//输入数据
    .dst_mac_add(dst_mac_add),//目的mac地址
    .src_mac_add(src_mac_add),//本地mac地址
    .type_length(type_length),//类型/长度
    .crc32(crc32),//效验码
    .data_req(data_req),//数据请求端口
    .ctrl_done(empty),//数据接收完成
    .tx_en(tx_en),//数据发送使能信号
    .tx_data(tx_data)//待发送的数据    
    );
    
endmodule
module mac_send(
    tx_clk,//输入时钟
    reset ,
    ctrl_tx_en,//输入使能端
    data_in,//输入数据
    dst_mac_add,//目的mac地址
    src_mac_add,//本地mac地址
    type_length,//类型/长度
    crc32,//效验码
    data_req,//数据请求端口
    ctrl_done,//数据接收完成
    tx_en,//数据发送使能信号
    tx_data//待发送的数据
    
    );
    input tx_clk;
    input reset ;
    input ctrl_tx_en;
    input [7:0]data_in;
    input [47:0]dst_mac_add;
    input [47:0]src_mac_add;
    input [15:0]type_length;
    input [31:0]crc32;
    output reg data_req;
    input ctrl_done;
    output reg tx_en;
    output reg [3:0]tx_data;
    
    //用状态机来实现
    //定义计时器
    reg [5:0]cnt;//最大45   
    
    reg mac_done ;//发送到45时产生一个mac帧结束信号
    always@(posedge tx_clk or negedge reset)
    if (reset)
        mac_done <= 0 ;  
    else if (cnt == 53)
        mac_done <= 1 ;  
    else if (mac_done)
        mac_done <= 0  ; 
        
    reg send_en ;
    always@(posedge tx_clk or negedge reset)
    if (reset)
        send_en <= 0 ;  
    else if (ctrl_tx_en)
        send_en <= 1 ;  
    else if (mac_done)
        send_en <= 0  ; 
        
             
    //数据段发完信号
    reg data_rec_done ;
    always@(posedge tx_clk or negedge reset)
    if (reset)
        data_rec_done <= 0 ;  
    else if (ctrl_done)
        data_rec_done <= 1 ;
    else if (data_rec_done)
        data_rec_done <= 0 ; 
        
      
    always@(posedge tx_clk or negedge reset)
    if (reset)
        cnt <= 0 ;   
    else if (send_en)
        begin
            if(( cnt < 53 )&&( cnt!=45 )&&(!mac_done) )
            cnt <= cnt + 1 ;
            else if ((cnt == 45) && (data_rec_done ))
            cnt <= cnt + 1;
            else if( cnt == 53 )
            cnt <= 0 ;
            else if (mac_done)
            cnt <= 0;
        end
    
    always@(posedge tx_clk or negedge reset)
    if (reset)
        data_req <= 0 ;   
    else if (send_en)
        begin
            if(cnt == 43 )
                data_req <= 1 ;
            else if ( cnt == 44 )
                data_req <= 0 ;
            else if ( cnt == 45 )
                data_req <= !data_req ;
        end
    
    
    always@(posedge tx_clk)
    if (reset ) 
        tx_data <= 4'h0 ;
    else if(send_en)
       case(cnt)
       //发送mac帧
       0:tx_data <= 4'h0;
       //前导符
       1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 : tx_data <= 4'h5 ;
       16:tx_data <= 4'hD ;
       
       //目的mac地址
       17:tx_data <= dst_mac_add[47:44];
       18:tx_data <= dst_mac_add[43:40];
       19:tx_data <= dst_mac_add[39:36];
       20:tx_data <= dst_mac_add[35:32];
       21:tx_data <= dst_mac_add[31:28];
       22:tx_data <= dst_mac_add[27:24];
       23:tx_data <= dst_mac_add[23:20];
       24:tx_data <= dst_mac_add[19:16];
       25:tx_data <= dst_mac_add[15:12];
       26:tx_data <= dst_mac_add[11:8];
       27:tx_data <= dst_mac_add[7:4];
       28:tx_data <= dst_mac_add[3:0];
       
       //源mac地址
       29:tx_data <= src_mac_add[47:44];
       30:tx_data <= src_mac_add[43:40];
       31:tx_data <= src_mac_add[39:36];
       32:tx_data <= src_mac_add[35:32];
       33:tx_data <= src_mac_add[31:28];
       34:tx_data <= src_mac_add[27:24];
       35:tx_data <= src_mac_add[23:20];
       36:tx_data <= src_mac_add[19:16];
       37:tx_data <= src_mac_add[15:12];
       38:tx_data <= src_mac_add[11:8];
       39:tx_data <= src_mac_add[7:4];
       40:tx_data <= src_mac_add[3:0];
       
       //长度/类型
       41:tx_data <= type_length[15:12];
       42:tx_data <= type_length[11:8];
       43:tx_data <= type_length[7:4];
       44:tx_data <= type_length[3:0];
      
       //数据填充
       45: begin
             if(!data_req)
                tx_data <= data_in[7:4];
             else if (data_req)
                tx_data <= data_in[3:0];
            end
       
       //校验位
       46: tx_data <= crc32[31:28];
       47: tx_data <= crc32[27:24];
       48: tx_data <= crc32[23:20];
       49: tx_data <= crc32[19:16];
       50: tx_data <= crc32[15:12];
       51: tx_data <= crc32[11:8];
       52: tx_data <= crc32[7:4];
       53: tx_data <= crc32[3:0];
       
       default: tx_data <= tx_data ;
       
       endcase
        
    always@(posedge tx_clk or negedge reset)
    if (reset)
        tx_en <= 0 ;
    else if( mac_done )
        tx_en <= 1 ;
    else 
        tx_en <= 0 ;
    
    
    
    
endmodule
`timescale 1ns / 1ns
module fifo_mac_send_tb(    
    );
    
    reg clk;
    reg reset ;
    reg [7:0]data_in ;
    reg  write_en ;
    reg [47:0]dst_mac_add;//目的mac地址
    reg [47:0]src_mac_add;//本地mac地址
    reg [15:0]type_length;//类型/长度
    reg [31:0]crc32;//效验码 
    reg ctrl_tx_en; 
    wire tx_en ;
    wire [3:0]tx_data ;
    
    fifo_mac_send fifo_mac_send_sim(
    clk,
    reset,
    data_in,
    write_en,
    dst_mac_add,
    src_mac_add,
    type_length,
    crc32,
    ctrl_tx_en,
    tx_en,//数据发送使能信号
    tx_data//待发送的数据    
    );
    
    initial clk = 1 ;
    always #10 clk = !clk ;
    initial begin
        reset = 1 ;
        data_in = 0;
        write_en = 0;
        dst_mac_add = 0 ;
        src_mac_add = 0 ;
        type_length = 0 ;
        crc32 = 0 ;
        ctrl_tx_en = 0 ;
        #201 ;
        reset = 0 ;
        #20 ;
        //向FIFO写入随机数据
        repeat(1024)begin
        write_en = 1;
        data_in = {$random} % 256 ;
        #20 ;
        end
        write_en = 0 ; 
        data_in = 0 ;
        #10000 ;
                
        //读FIFO数据
        ctrl_tx_en = 1 ;
        dst_mac_add = 48'hff_ff_ff_ff_ff_ff ;
        src_mac_add = 48'h08_8f_c3_41_af_2a ;
        type_length = 16'd1024 ;
        crc32 = 32'h55_55_55_dd ;
        #20 ;
        ctrl_tx_en = 0;
        #100000;
        
        
        //第二个mac帧
        reset = 1 ;
        data_in = 0;
        write_en = 0;
        dst_mac_add = 0 ;
        src_mac_add = 0 ;
        type_length = 0 ;
        crc32 = 0 ;
        ctrl_tx_en = 0 ;
        #201 ;
        reset = 0 ;
        #20 ;
        //向FIFO写入随机数据
        repeat(1024)begin
        write_en = 1;
        data_in = {$random} % 256 ;
        #20 ;
        end
        write_en = 0 ; 
        data_in = 0 ;
        #10000 ;
                
        //读FIFO数据
        ctrl_tx_en = 1 ;
        dst_mac_add = 48'h12_34_56_78_9a_bc ;
        src_mac_add = 48'h08_8f_c3_41_af_2a ;
        type_length = 16'd1024 ;
        crc32 = 32'h5d_5d_5d_cb ;
        #20 ;
        ctrl_tx_en = 0;
        #100000;
        
        
        $stop;        
    end
    
endmodule

4.仿真结果

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值