60.以太网数据回环实验(3)以太网数据收发器接收模块

        (1)状态设计:

  • ST_IDLE        :空闲状态
  • ST_PREAMBLE     :前导码+帧起始界定符
  • ST_ETH_HEAD     :以太网帧头:目标MAC地址+源MAC地址+类型/长度
  • ST_IP_HEAD         :IP首部
  • ST_UDP_HEAD     :UDP首部(8个字节)
  • ST_RX_DATA        :接收数据状态
  • ST_RX_DONE      :接收完成状态

        (2)状态转移图: 

        (3)以太网帧头:目标MAC地址(6个字节)+源MAC地址(6个字节)+ 类型/长度(2个字节)(08 00 表示基于IP协议)      目标MAC地址需要使用一个寄存器寄存起来。

        (4)IP首部:目的 IP 地址在首部的第 17 至 20字节,需要使用一个寄存器寄存起来。

        比如第一个字节为45,则IP地址长度对应5左移两位(010100),即长度为20

       (5)UDP首部:第五个字节和第六个字节包含了UDP长度信息,例如:0028(40),有效数据长度等于UDP长度-首部长度(8个字节)

        (6)UDP接收模块代码:

//UDP接收模块设计

module udp_rx(

    input   wire                gmii_rxc            ,
    input   wire                reset_n             ,
    input   wire                gmii_rx_dv          ,   //GMII输入数据有效信号
    input   wire    [7:0]       gmii_rxd            ,   //GMII输入数据
    output  reg     [31:0]      rec_data            ,   //以太网接收后转化为32位的数据
    output  reg                 rec_en              ,   //以太网接收的数据使能信号
    output  reg                 rec_pkt_done        ,   //以太网单包数据接收完成信号 
    output  reg     [15:0]      rec_byte_num            //以太网接收的有效数据字节数

);

/*--------------参数定义,parameter全局变量,可以在上层进行修改,而localparam则是局部变量,仅在该模块使用-----------*/
//开发板MAC地址(48位)
parameter BOARO_MAC     =   48'hff_ff_ff_ff_ff_ff       ;
 
//开发板IP地址(32位)
parameter BOARO_IP      =   {8'd0,8'd0,8'd0,8'd0}       ;
 
//状态机状态定义 独热吗编码
localparam ST_IDLE      =   7'b000_0001     ;           //空闲状态,等待接收前导码
localparam ST_PREAMBLE  =   7'b000_0010     ;           //接收前导码和帧起始界定符状态
localparam ST_ETH_HEAD  =   7'b000_0100     ;           //接收以太网帧头状态
localparam ST_IP_HEAD   =   7'b000_1000     ;           //接收IP首部状态
localparam ST_UDP_HEAD  =   7'b001_0000     ;           //接收UDP首部
localparam ST_RX_DATA   =   7'b010_0000     ;           //接收有效数据状态
localparam ST_RX_DONE   =   7'b100_0000     ;           //接收完成状态
    
//以太网协议类型定义  IP协议
localparam ETH_TYPE     =   16'h0800        ;

/*--------------寄存器变量定义-----*/
reg     [6:0]       cur_state       ;           //当前状态
reg     [6:0]       next_state      ;           //下一个状态

reg                 sw_en           ;           //下一个状态跳转标志信号
reg                 error_en        ;           //接收错误标志信号  
reg     [4:0]       cnt_byte        ;           //字节计数器
reg     [47:0]      des_mac         ;           //目标MAC地址,在本模块中及开发板MAC地址
reg     [15:0]      eth_type        ;           //以太网类型
reg     [31:0]      des_ip          ;           //目标IP地址,在本模块中及开发板IP地址
reg     [5:0]       ip_len          ;           //IP首部长度
reg     [15:0]      udp_len         ;           //UDP长度
reg     [15:0]      cnt_data        ;           //数据长度计算    
reg     [15:0]      data_len        ;           //UDP有效数据长度
reg     [1:0]       cnt_rec_data    ;           //8位转32位计数器

/*--------------三段式状态机编写主程序--------*/
//第一段:时序逻辑描述状态转移
always@(posedge gmii_rxc or negedge reset_n)begin
    if(!reset_n)
        cur_state <=  ST_IDLE   ;
    else 
        cur_state <= next_state ;
end

//第二段:组合逻辑判断状态机转移条件
always@(*)begin
    next_state = ST_IDLE  ;
    case(cur_state)
        ST_IDLE :
            if(sw_en)
                next_state = ST_PREAMBLE;
            else 
                next_state = ST_IDLE;
        ST_PREAMBLE :
            if(sw_en)
                next_state = ST_ETH_HEAD;
            else if(error_en)
                next_state = ST_RX_DONE;
            else 
                next_state = ST_PREAMBLE;
        ST_ETH_HEAD :
            if(sw_en)
                next_state = ST_IP_HEAD;
            else if(error_en)
                next_state = ST_RX_DONE;
            else 
                next_state = ST_ETH_HEAD;
        ST_IP_HEAD  :
            if(sw_en)
                next_state = ST_UDP_HEAD;
            else if(error_en)
                next_state = ST_RX_DONE;
            else 
                next_state = ST_IP_HEAD;
        ST_UDP_HEAD :
            if(sw_en)
                next_state = ST_RX_DATA;
            else 
                next_state = ST_UDP_HEAD;
        ST_RX_DATA :
            if(sw_en)
                next_state = ST_RX_DONE;
            else 
                next_state = ST_RX_DATA;
        ST_RX_DONE :
            if(sw_en)
                next_state = ST_IDLE;
            else 
                next_state = ST_RX_DONE;
        default:next_state = ST_IDLE  ;
    endcase           
end
  
//第三段:时序逻辑描述寄存器变量和状态输出
always@(posedge gmii_rxc or negedge reset_n)begin
    if(!reset_n)begin
        sw_en         <= 1'd0;        
        error_en      <= 1'd0;   
        cnt_byte      <= 5'd0;   
        des_mac       <= 48'd0;   
        eth_type      <= 16'd0;   
        des_ip        <= 32'd0;
        ip_len        <= 6'd0;
        udp_len       <= 16'd0;   
        cnt_data      <= 16'd0;   
        data_len      <= 16'd0;   
        cnt_rec_data  <= 2'd0;   
        rec_data      <= 32'd0;
        rec_en        <= 1'd0;
        rec_pkt_done  <= 1'd0;
        rec_byte_num  <= 16'd0;   
    end
    else begin
        sw_en         <= 1'd0;        
        error_en      <= 1'd0;
        rec_en        <= 1'd0;
        rec_pkt_done  <= 1'd0;
        case(next_state)
            ST_IDLE :begin                      //GMII输入有效信号拉高,接收到0x55,跳转至下一个状态
                if((gmii_rx_dv == 1'd1) && (gmii_rxd == 8'h55))
                    sw_en <= 1'd1;
            end              
            ST_PREAMBLE :begin
                if(gmii_rx_dv == 1'd1)begin
                    cnt_byte <= cnt_byte + 5'd1;                    
                    if((cnt_byte < 5'd6) && (gmii_rxd != 8'h55))    //如果正确,0,1,2,3,4,5 再加上空闲跳转到该状态的一个0x55,总共7个0x55  
                        error_en <= 1'd1;
                    else if(cnt_byte == 5'd6)begin
                        cnt_byte <= 5'd0;
                            if(gmii_rxd == 8'hd5)           //一个0xd5
                                sw_en <= 1'd1;
                            else    
                                error_en <= 1'd1;
                    end
                end
            end    
            ST_ETH_HEAD :begin
                if(gmii_rx_dv == 1'd1)begin
                    cnt_byte <= cnt_byte + 5'd1;
                    if(cnt_byte < 5'd6)                      //0,1,2,3,4,5  6个8位数据,并凑出一个48位的MAC地址
                        des_mac <= {des_mac[39:0],gmii_rxd};
                    else if(cnt_byte == 5'd12)               //6,7,8,9,10,11 6个8位数据,是源MAC地址,12,13位为协议类型
                        eth_type[15:8]  <= gmii_rxd;
                    else if(cnt_byte == 5'd13)begin
                        eth_type[7:0]   <= gmii_rxd;
                        cnt_byte        <= 5'd0;
                        if(((des_mac == BOARO_MAC) || (des_mac == 48'hff_ff_ff_ff_ff_ff))
                            && eth_type[15:8] == ETH_TYPE[15:8]
                            && gmii_rxd == ETH_TYPE[7:0])
                                sw_en <= 1'd1;
                        else 
                            error_en <= 1'd1;
                    end
                end
            end
            ST_IP_HEAD :begin
                if(gmii_rx_dv == 1'd1)begin
                    cnt_byte <= cnt_byte + 5'd1;            
                    if(cnt_byte == 5'd0)            //IP首部第一个字节的后四位左移2位 代表IP首部的长度
                        ip_len <= {gmii_rxd[3:0],2'd0};           
                    else if((cnt_byte >= 5'd16) && (cnt_byte <= 5'd18))      //IP首部的后四个字节组成目标IP地址
                        des_ip <= {des_ip[23:0],gmii_rxd};
                    else if(cnt_byte == 5'd19)begin
                        des_ip <= {des_ip[23:0],gmii_rxd};
                        if((des_ip[23:0] == BOARO_IP[31:8])
                            &&(gmii_rxd == BOARO_IP[7:0]))
                                if(cnt_byte == ip_len - 1'b1)begin
                                    sw_en <= 1'd1;
                                    cnt_byte <= 5'd0;                                
                                end
                                else begin
                                    error_en <= 1'd1;
                                    cnt_byte <= 5'd0;
                                end
                    end
                    else if(cnt_byte == ip_len - 1'b1)begin
                        sw_en <= 1'd1;
                        cnt_byte <= 5'd0;  
                    end
                end
            end 
            /*------UDP首部(8个字节):第五个字节和第六个字节包含了UDP长度信息,
            例如:0028(40),有效数据长度等于UDP长度-首部长度(8个字节)----*/
            ST_UDP_HEAD :begin
                if(gmii_rx_dv == 1'd1)begin
                    cnt_byte <= cnt_byte + 5'd1;
                    if(cnt_byte == 5'd4)
                        udp_len[15:8]   <= gmii_rxd;
                    else if(cnt_byte == 5'd5)
                        udp_len[7:0]    <= gmii_rxd;
                    else if(cnt_byte == 5'd7)begin
                        data_len <= udp_len - 16'd8;
                        sw_en   <= 1'd1;
                        cnt_byte <= 5'd0;
                    end 
                end
            end
            ST_RX_DATA  :begin
                if(gmii_rx_dv == 1'd1)begin
                    cnt_data <= cnt_data + 16'd1;
                    cnt_rec_data <= cnt_rec_data + 2'd1;
                    if(cnt_data == data_len - 16'd1)begin
                        sw_en <= 1'd1;
                        cnt_data <= 16'd0;
                        cnt_rec_data <= 2'd0;
                        rec_pkt_done <= 1'd1;
                        rec_en <= 1'd1;
                        rec_byte_num <= data_len;                   
                    end
                    if(cnt_rec_data == 2'd0)
                        rec_data[31:24] <= gmii_rxd;
                    else if(cnt_rec_data == 2'd1)
                        rec_data[23:16] <= gmii_rxd;
                    else if(cnt_rec_data == 2'd2)
                        rec_data[15:8]  <= gmii_rxd;
                    else if(cnt_rec_data == 2'd3)begin
                        rec_en <= 1'd1;
                        rec_data[7:0] <= gmii_rxd;
                    end
                end
            end
            ST_RX_DONE  :begin       //单包数据接收完成
                if((gmii_rx_dv == 1'd0) && (sw_en == 1'd0))
                    sw_en <= 1'd1;
            end
            default:   ;      
        endcase
    end
end
    
endmodule

        (7)仿真代码:

`timescale 1ns / 1ps

module tb_udp_rx;

 reg                gmii_rxc        ;        
 reg                reset_n         ;  
 reg                start_flag      ;
 reg                gmii_rx_dv      ;
 reg    [7:0]       cnt             ;
 reg    [7:0]       data_mem[85:0]  ;
 
 wire   [7:0]       gmii_rxd        ; 
 wire   [31:0]      rec_data        ;     
 wire               rec_en          ;     
 wire               rec_pkt_done    ;     
 wire   [15:0]      rec_byte_num    ;


initial gmii_rxc = 1'd1;
always #10 gmii_rxc = ~gmii_rxc;

initial begin
    reset_n <= 1'd0;
    start_flag <= 1'd0;
    #200
    reset_n <= 1'd1;
    #200
    start_flag <= 1'd1;
    #40
    start_flag <= 1'd0;
    #3000
    $stop;
end 

initial $readmemh("D:/FPGA/35_eth_udp_loop/vivado_prj/project_1.srcs/sim_1/new/data.txt",data_mem);

assign gmii_rxd = (gmii_rx_dv == 1'd1)? data_mem[cnt] : 8'd0;

always@(negedge gmii_rxc or negedge reset_n)
    if(!reset_n)
        cnt <= 1'd0;
    else if(gmii_rx_dv == 1'd1)
        cnt <= cnt + 1'b1;
    else 
        cnt <= cnt;
        
always@(negedge gmii_rxc or negedge reset_n)
    if(!reset_n)
        gmii_rx_dv <= 1'd0;
    else if(cnt == 8'd85)                   //8 + 14 + 20 + 8 + 32 + 4 = 86
        gmii_rx_dv <= 1'd0;
    else if(start_flag)
        gmii_rx_dv <= 1'd1;
    else 
        gmii_rx_dv <= gmii_rx_dv;
    

defparam udp_rx_inst.BOARO_MAC  = 48'h12_34_56_78_9a_bc;
defparam udp_rx_inst.BOARO_IP   = 32'ha9_fe_01_17 ;

udp_rx  udp_rx_inst(

    .gmii_rxc            (gmii_rxc       ),
    .reset_n             (reset_n        ),
    .gmii_rx_dv          (gmii_rx_dv     ),   
    .gmii_rxd            (gmii_rxd       ),   
    .rec_data            (rec_data       ),   
    .rec_en              (rec_en         ),   
    .rec_pkt_done        (rec_pkt_done   ),   
    .rec_byte_num        (rec_byte_num   )    

);






endmodule

        (8)仿真波形:

  • 7
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值