Menu Close

MAC层接收 verilog 工程(网络综合实验)

图1所示为网络综合实验框图,其中红框部分是MAC层的接收(eth_mac_rx.v)。

图1 网络综合实验框图

 

图2所示为MAC层的接收(eth_mac_rx.v)。

图2 eth_mac_rx.v

 

MAC层的接收(eth_mac_rx.v)流程有以下的步骤:

  • 接收PHY传递过来的数据包:
    • 判断并拆除前导字(preamble)
    • 判断数据包的大小是否满足IEEE 802.3的定义(数据包长度小于1518,且大于64个字节)
    • 判断数据是否有效
    • 判断数据包之间的GAP是否至少为9
  • 将处理过的数据递进FIFO,注意FIFO里的数据是9-bit,低位8-bit是传输的数据,最高位bit的作用是隔断数据包和标记出错误的数据包。
  • 计算数据包的CRC
  • 将处理过的数据传递进8/16的AB buffer,为之后的数据处理提供更高的效率

注意,由于MAC层的接收比较复杂,所以专门创建了一个mac_error的寄存器,每一个bit的定义如下:

  • mac_error[0] : 以太网数据包包头错误
  • mac_error[1] : FIFO满
  • mac_error[2] : 以太网数据包长度大于1518字节
  • mac_error[3] : 以太网数据包长度小于64字节
  • mac_error[4] : 以太网数据包 gap 错误
  • mac_error[5] : 保留
  • mac_error[6] : 保留
  • mac_error[7] : 以太网数据包CRC错误

 

eth_mac_rx.v的代码如下(这里省略了CRC模块代码,详情可见文章互联网MAC层CRC32 Check Verilog 代码):

`timescale 1ns / 1ps

module eth_mac_rx #
(
    parameter IP_BUF_SIZE = 10,
    parameter IP_IDX_BIT  = 1
)
(
    input         mac_rx_clk,
    input  [7:0]  mac_rx_data,
    input         mac_rx_toggle,
    input         mac_rx_dv,

    input         sys_clk,

    //used by A/B buffer
    input         ip_rx_web,
    input  [IP_BUF_SIZE + IP_IDX_BIT - 1:0] ip_rx_addrb,
    output [15:0] ip_rx_doutb,

    //mac_error register
    output reg [7:0] mac_error = 0,

    input         reset
);
    
//====================FIFO SIGNALS=======================================    
reg [8:0]  eth_rx_fifo_din = 0;
reg        eth_rx_fifo_we  = 0;
wire       eth_rx_fifo_full;
//=======================================================================    
reg [7*8 - 1 : 0] chk_pream = 0;         //used to check the preamble
reg [4:0] rx_mac_err = 0;                //lower 5 bits of mac_error register

reg [2:0] rx_mac_st = 0;                 //state machine
reg [10:0] rx_cnt = 0;                   //count
always @ (posedge mac_rx_clk or posedge reset)
if(reset)
begin
    rx_cnt <= 0;
    rx_mac_st <= 0;
end
else case (rx_mac_st)
0:
begin
    rx_mac_err <= 0;
    chk_pream <= 0;
    eth_rx_fifo_din <= 0;
    eth_rx_fifo_we  <= 0;

    rx_cnt <= rx_cnt + 1;                //hold for fifo reset
    if(&rx_cnt)
    begin
        rx_mac_st <= 1;
    end
end
1:
begin
    rx_mac_err <= 0;
    eth_rx_fifo_we  <= 0;
    rx_cnt <= 0;
    if(mac_rx_dv)
    begin
        if(mac_rx_toggle)
        begin
            chk_pream <= {chk_pream[6*8 - 1: 0] , mac_rx_data};         //check the preamble
            rx_mac_st <= 2;
        end
    end
    else chk_pream <= 0;
end
2:
begin
    if(mac_rx_dv)
    begin
        if(mac_rx_toggle)
        begin
            chk_pream <= {chk_pream[6*8 - 1: 0] , mac_rx_data};         //check the preamble
            if({chk_pream[6*8 - 1: 0] , mac_rx_data} == {8'h55,8'h55,8'h55,8'h55,8'h55,8'h55,8'hd5})
                rx_mac_st <= 3;
        end
    end
    else         //if the preamble is not correct, flag the mac_error[0] ===> eth packet header error
    begin
        rx_mac_err[0] <= 1;
        rx_mac_st <= 7; 
    end
end
3:
begin
    chk_pream <= 0;          //reset the preamble check, so that the following packet header could be checked again

    if(eth_rx_fifo_full)     //if fifo is full, flag the mac_error[1] ===> fifo full error
    begin
        eth_rx_fifo_we <= 0;
        rx_mac_err[1] <= 1;
        rx_mac_st <= 7;  // fifo full error
    end
    else              //if fifo is not full, fill the fifo with eth packet
    begin
        if(mac_rx_dv)
        begin
            if(mac_rx_toggle)
            begin
                eth_rx_fifo_din <= {1'b0, mac_rx_data}; 
                eth_rx_fifo_we  <= 1;
                rx_cnt <= rx_cnt + 1;             //count the eth packet size 
                if(rx_cnt == 1518)                //if excluding preamble, eth packet max length is 1518 bytes 
                begin
                    rx_mac_err[2] <= 1;        // if packet > 1518, flag the mac_error[2] ===> eth packet > 1518
                    rx_mac_st <= 7;            //handle the error   
                end
            end
            else eth_rx_fifo_we  <= 0;
        end
        else        //rx_err or packet end
        begin
            eth_rx_fifo_we  <= 0;
            rx_mac_st <= 4;
        end
    end
end
4:
begin
    if(eth_rx_fifo_full || (rx_cnt < 64 ) )    //if fifo full or eth packet length smaller than the min length
    begin
        eth_rx_fifo_we <= 0;
        rx_mac_err[1] <= eth_rx_fifo_full;     //if fifo is full, flag the mac_error[1] ===> fifo full error
        rx_mac_err[3] <= (rx_cnt < 64 ) ? 1'b1 : 1'b0;      //if eth packet < 64, flag the mac_error[3] ===> eth packet < 64

        rx_mac_st <= 7;  //handle the error 
    end
    else    //error or packet end
    begin
        if(!mac_rx_dv)
        begin
            rx_cnt <= 2;
            eth_rx_fifo_we  <= 0;
            rx_mac_st <= 5;       //packet end
        end
        else 
        begin
            rx_mac_err[4] <= 1;  //flag the mac_error[4] ===> eth packet gap error
            rx_mac_st <= 7;      //handle the error
        end
    end
end
5:      //packet end
begin
    if(eth_rx_fifo_full)      //if fifo is full, flag the mac_error[1] ===> fifo full error
    begin
        eth_rx_fifo_we <= 0;
        rx_mac_err[1] <= 1;
        rx_mac_st <= 7;      // handle the error
    end
    else
        begin
        if(!mac_rx_dv)
        begin 
            rx_cnt <= rx_cnt + 1;   //count the gap between eth packets
            if(rx_cnt == 9)         // ifg(interframe gap)  ifg >= 9 
            begin
                eth_rx_fifo_din <= {1'b1, 8'hff};  // normal end flag
                eth_rx_fifo_we  <= 1;
                
                rx_mac_st <= 1;     //ready to receive new eth packet
            end
        end
        else    //if the eth packet gap is not enough
        begin
            rx_mac_err[4] <= 1;   //flag the mac_error[4] ===> eth packet gap error
            rx_mac_st <= 7;       // handle the error
        end
    end
end 
7: // handle the error flag
begin
    chk_pream <= 0;    //reset the registers
    rx_cnt <= 0;

    if(!eth_rx_fifo_full) 
    begin
        eth_rx_fifo_din <= {1'b1, 8'h00};    // send error end flag
        eth_rx_fifo_we  <= 1;

        rx_mac_st <= 1;       //ready to receive new eth packet
    end
    else     //if fifo is full, wait here
        eth_rx_fifo_we  <= 0;
    
end
default: rx_mac_st <= 1;
endcase

//======================= FIFO related signals ==========================  
wire [11:0] eth_rx_fifo_cnt;
wire [8:0]  eth_rx_fifo_dout;
reg         eth_rx_fifo_rd = 0;
wire        eth_rx_fifo_vld;
wire        eth_rx_fifo_empty;

ETH_RX_FIFO   ETH_RX_FIFO_inst
(
    .wr_clk (mac_rx_clk),           // input  wire wr_clk
    .din    (eth_rx_fifo_din),      // input  wire [8 : 0] din
    .wr_en  (eth_rx_fifo_we),       // input  wire wr_en
    .full   (eth_rx_fifo_full),     // output wire full

    .rd_clk (sys_clk),              // input  wire rd_clk
    .rd_en  (eth_rx_fifo_rd),       // input  wire rd_en
    .valid  (eth_rx_fifo_vld),      // output wire valid
    .dout   (eth_rx_fifo_dout),     // output wire [8 : 0] dout
    .empty  (eth_rx_fifo_empty),    // output wire empty

    .rd_data_count(eth_rx_fifo_cnt),// output wire [11 : 0] wr_data_count
    .rst    (reset)                 // input  wire rst
);    
//=====================A/B Buffer related signals======================================   
localparam MAC_RXBUF_SIZE = IP_BUF_SIZE + 1;    //here 11, address can be up to 2048
localparam MAC_RXIDX_BIT  = IP_IDX_BIT;         //here 1


reg  ip_rx_wea = 0;
reg  [MAC_RXBUF_SIZE - 1:0] ip_rx_addra_sub = 0;
reg  [MAC_RXIDX_BIT - 1:0] ip_rx_ida = 0;
// size is related to ip_rx_addrb
wire [MAC_RXBUF_SIZE + MAC_RXIDX_BIT - 1:0] ip_rx_addra = {ip_rx_ida, ip_rx_addra_sub};  

reg  [7:0] ip_rx_dina = 0;
wire [7:0] ip_rx_douta;
//=======================================================================   
reg  crc_err_flag = 0;
wire crc_err;      //output from CRC module
reg  crc_init = 0;
reg  crc_en = 0;   //used to enable CRC
reg  [2:0] rx_fifo_st = 0;    //state machine
reg  [15:0] rx_fifo_cnt = 0;
always @ (posedge sys_clk)
if(reset)
begin
    ip_rx_ida <= 0;
    ip_rx_wea <= 0;
    eth_rx_fifo_rd <= 0;
    rx_fifo_st <= 0;
end 
else case (rx_fifo_st)
0:
begin
    crc_err_flag <= 0;
    crc_en <= 0;
    rx_fifo_cnt <= 0;
    eth_rx_fifo_rd <= 0;
    ip_rx_wea <= 0;
    ip_rx_addra_sub <= {MAC_RXBUF_SIZE{1'b1}};          //addr 2047
    rx_fifo_st <= 1;
end
1:
begin
    crc_init <= 1;      //initialize the CRC
    ip_rx_wea <= 0;
    ip_rx_addra_sub <= {MAC_RXBUF_SIZE{1'b1}};
    rx_fifo_st <= 2;
end
2:     //read from A/B Buffer, for address 2047, read after 2 clocks
begin
    crc_init <= 0;
    if(ip_rx_douta[7] == 0)       //indicates A/B buffer is available to write
        rx_fifo_st <= 3;
end
3:
begin
    ip_rx_wea <= 0;
    eth_rx_fifo_rd <= 1;      //read data from FIFO
    if(eth_rx_fifo_vld)
    begin
        if(eth_rx_fifo_dout[8] == 0) // normal data
        begin
            ip_rx_addra_sub <= ip_rx_addra_sub + 1;   //move up the address, write 8 bits every time from port A
            ip_rx_dina <= eth_rx_fifo_dout[7:0];
            rx_fifo_cnt <= rx_fifo_cnt + 1;           //count the data read from FIFO
            crc_en <= 1;
            ip_rx_wea <= 1;
        end
        else //eth_rx_fifo_dout[8] == 1, ===> end packet segment
        begin
            eth_rx_fifo_rd <= 0;
            crc_en <= 0;
            ip_rx_wea <= 0;
            if(eth_rx_fifo_dout[7:0] == 8'hff)        // packet end
                rx_fifo_st <= 4;                      //proceed to next step
            else // eth_rx_fifo_dout[7:0] == 8'h00, ===> mac packet error
            begin
                rx_fifo_st <= 0;
            end
        end
    end
end
4:
begin
    if(crc_err)     //if CRC error, restart
    begin
        crc_err_flag <= 1;
        rx_fifo_st <= 0;
    end
    else            //proceed to next step
    begin
        rx_fifo_cnt <= rx_fifo_cnt - 4; // ip packet length = mac packet length - 4(crc) FCS
        rx_fifo_st <= 5;
    end
end
5:
begin
    crc_err_flag <= 0;
    ip_rx_addra_sub <=  {{(MAC_RXBUF_SIZE-1){1'b1}}, 1'b0};    //addr 2046 {10'b1,1'b0}
    ip_rx_dina <= rx_fifo_cnt[7:0];                    //packet size
    ip_rx_wea <= 1;
    rx_fifo_st <= 6;
end
6:
begin
    ip_rx_addra_sub <=  {MAC_RXBUF_SIZE{1'b1}};      // addr 2047
    ip_rx_dina <= {4'b1000, rx_fifo_cnt[11:8]};      //flag and the packet size following bit
    ip_rx_wea <= 1;
    rx_fifo_st <= 7;
end
7:
begin
    ip_rx_ida <= ip_rx_ida + 1;          //proceed to next buffer
    ip_rx_wea <= 0;
    rx_fifo_st <= 0;
end
default: rx_fifo_st <= 0;
endcase
//======================= CRC related signals =========================   
wire crc_enable = crc_en & eth_rx_fifo_vld;
CRC_chk  CRC_chk_inst
(
    .clk              (sys_clk),
//	.crc_data         (eth_rx_fifo_dout[7:0]),
    .crc_data         (ip_rx_dina),
    
    .crc_init         (crc_init),
    .crc_en           (crc_enable),
    .crc_chk_en       (1'b1),
    .crc_err          (crc_err),
    .rst              (reset)
);
//======================== A/B Buffer =================================   
NET_DRAM   IP_RX_DRAM_inst
(
    .clka   (sys_clk),      // input wire clka
    .wea    (ip_rx_wea),    // input wire [0 : 0] wea
    .addra  (ip_rx_addra),  // input wire [11 : 0] addra
    .dina   (ip_rx_dina),   // input wire [7 : 0] dina
    .douta  (ip_rx_douta),  // output wire [7 : 0] douta
    
    .clkb   (sys_clk),      // input wire clkb
    .web    (ip_rx_web),    // input wire [0 : 0] web
    .addrb  (ip_rx_addrb),  // input wire [10 : 0] addrb
    .dinb   (0),            // input wire [15 : 0] dinb
    .doutb  (ip_rx_doutb)   // output wire [15 : 0] doutb
);

//======================================================================    
//     save the mac_error register
always @ (posedge sys_clk)
    mac_error <= {crc_err_flag, 2'b00, rx_mac_err};
endmodule

 

其中有rx_mac_st和rx_fifo_st两个状态机。rx_mac_st用来处理PHY层发过来的数据包,并将提取出来的数据送进FIFO。rx_fifo_st是将FIFO里的值写入A/B Buffer。接下来对这两个状态机进行分析如下:

rx_mac_st:

图3所示为rx_mac_st状态机的简易状态转移图(注意省略了状态转移条件不满足时,停留在当前状态的箭头)。

  • 状态0是复位状态
  • 状态1开始接收数据包
  • 状态2负责检查数据包的前导字
  • 状态3将提取出的数据写进FIFO
  • 转换4和状态5负责处理数据包快结束的时候可能会出现的错误,比如FIFO已经满,数据包长度不符合IEEE 802.3的标准等
  • 状态7用来处理在状态2,3,4,5中所有可能出现的错误,并在FIFO封包时将错误标志立起来

图3 rx_mac_st流程图

 

rx_fifo_st:

图4所示为rx_fifo_st状态机的简易状态转移图(注意省略了状态转移条件不满足时,停留在当前状态的箭头)。

  • 状态0和状态1指定了读A/B Buffer的地址,因为A/B Buffer是双端口RAM,需要等待两个时钟周期才能成功读出数据
  • 状态2判断读出的标志,指示当前A/B Buffer是否可写
  • 状态3将FIFO的数据读出,写进A/B Buffer
  • 状态4判断CRC
  • 状态5和6将数据总长度,A/B Buffer可读的标志写进A/B Buffer
  • 状态7转到A/B Buffer另一侧,重新准备写入数据

 

 

图4 rx_fifo_st流程图

Posted in FPGA, FPGA 教材教案, Vivado, 开发工具, 教材与教案, 文章

2 Comments

  1. William

    都是可以的, 关键时rx_mac_err = 1 持续几个rx_clk。 rx_mac_err 是用来提供错误信息的。 可以根据自己的需要,更改错误信号持续的时间

发表回复

相关链接