[X]关闭

[米联客-安路飞龙DR1-FPSOC] UDP通信篇连载-02 MAC层程序设计

文档创建者:FPGA课程
浏览次数:384
最后更新:2024-08-09
文档课程分类-安路-DR1
安路-DR1: FPSOC-DR1-FPGA部分 » 2_FPGA实验篇(仅旗舰) » 4-FPGA UDP通信
软件版本:Anlogic -TD5.9.1-DR1_ES1.1
操作系统:WIN10 64bit
硬件平台:适用安路(Anlogic)FPGA
实验平台:米联客-MLK-L1-CZ06-DR1M90G开发板
板卡获取平台:https://milianke.tmall.com/
登录"米联客"FPGA社区 http://www.uisrc.com 视频课程、答疑解惑!
3 程序设计
前面我们介绍了以太网的基本概念,及涉及的各层协议格式,接下来我们通过设计Verilog程序来实现以太网各个子层的功能。程序整体架构图如下:
2504661-20240809185149154-59462527.jpg
3.1 MAC
MAC层一边连接GMII接口,一边连接上层协议传来的数据。该层将上层传来的数据包组帧发送出去,或者将接收到的数据帧解析,将信息和拆解出来的数据包传给上层协议。
3.1.1 MAC接收模块
MAC接收模块主要实现以下几个功能:
(1)对接收到的MAC帧进行解析,过滤前导码、帧起始定界符、MAC地址、类型、CRC校验位,将上层数据包提取出来,并缓存数据包类型,传给上层做判断。
(2)对每帧数据进行CRC校验,与帧末尾的4字节校验位做比较,判断数据的正确性
(3)识别接收到的流控帧,将有效信息发送给子层解析,把解析到的暂停时间和源MAC地址输出至MAC接收模块做进一步处理。
(4)通过FIFO完成PHY接收时钟和用户接口时钟之间的时钟域的转换,并将数据包输出至上层。
  1. /*******************************uimac_rx模块*********************
  2. --以下是米联客设计的uimac_rx模块
  3. --本模块主要有以下几个功能
  4. --1. 从外部PHY芯片接收mac帧,解析mac帧首部,进行mac地址过滤和帧类型过滤。
  5. --2. 内部子模块crc32_check对每帧数据进行crc32值的计算,判断数据的正确性。
  6. --3. 识别接收的mac流控帧,子模块mac_frame_ctrl提取流控帧中的暂停时间和源mac地址输出至uimac_tx模块。
  7. --4. mac_rx_data_fifo完成phy接收时钟和用户接口时钟之间的时钟域转换,将数据输出。
  8. *********************************************************************/
  9. `timescale  1ns/1ps
  10. module  uimac_rx
  11. (
  12.     input   wire    [47:0]      I_mac_local_addr    ,   //本地MAC地址
  13.     input   wire                I_crc32_en          ,   //使能CRC校验
  14.     input   wire                I_reset             ,   //系统复位
  15.     //MAC接收数据发送给上层协议
  16.     input   wire                I_mac_rclk          ,   //接收时钟
  17.     output  wire                O_mac_rvalid        ,   //MAC帧数据有效
  18.     output  wire    [7:0]       O_mac_rdata         ,   //MAC有效数据
  19.     output  wire    [15:0]      O_mac_rdata_type    ,   //MAC类型
  20.     output  wire                O_mac_rdata_error   ,
  21.     //发送PAUSE控制到mac_send
  22.     output  wire                O_mac_pause_en      ,
  23.     output  wire    [21:0]      O_mac_pause_time    ,
  24.     output  wire    [47:0]      O_mac_pause_addr    ,
  25.     //从硬件层获取的裸MAC数据
  26.     input   wire                I_gmii_rclk         ,   //rgmii接收时钟
  27.     input   wire                I_gmii_rvalid       ,   //gmii接收数据有效使能信号
  28.     input   wire    [7:0]       I_gmii_rdata            //gmii接收数据
  29. );

  30. wire    [7:0]       mac_rdata;
  31. reg                 mac_rdata_valid;
  32. reg     [15:0]      mac_rdata_type;
  33. reg                 mac_rdata_error;

  34. assign  O_mac_rdata         =   mac_rdata;
  35. assign  O_mac_rvalid        =   mac_rdata_valid;
  36. assign  O_mac_rdata_type    =   mac_rdata_type;
  37. assign  O_mac_rdata_error   =   mac_rdata_error;

  38. reg     [10:0]      mac_rdata_cnt;
  39. reg                 mac_wfifo_en;   //FIFO写入数据有效使能
  40. reg                 mac_rfifo_en;   //FIFO读数据使能

  41. reg                 crc_en;         //crc校验使能
  42. reg     [2:0]       crc_cnt;        //移位计数器
  43. wire    [31:0]      crc_data_out;   //crc校验结果输出
  44. reg     [2:0]       STATE;          //写FIFO状态机
  45. reg     [1:0]       S_RFIFO;        //读FIFO状态机

  46. reg     [47:0]      dst_mac_addr;   //MAC帧解析出的目的MAC地址(接收方的MAC地址)
  47. reg     [47:0]      src_mac_addr;   //MAC帧解析出的源MAC地址(发送方的MAC地址)
  48. reg     [15:0]      mac_frame_type; //上层数据包类型(0x0800 ip;0x0806 arp;0x8808 mac_ctrl)
  49. reg                 mac_pause_en;   //PAUSE帧有效使能
  50. reg     [3:0]       cnt;            //对MAC帧头部的字节数计数

  51. reg     [10:0]      mac_wfifo_data_cnt_info;//写帧信息字节数到信息FIFO
  52. reg                 mac_wfifo_en_info;      //写帧信息FIFO有效使能
  53. reg                 mac_rfifo_en_info;      //读帧信息FIFO有效使能
  54. wire    [26:0]      mac_rfifo_data_info;    //从信息FIFO读出帧信息
  55. reg     [10:0]      mac_rdata_len;          //mac帧长度
  56. wire                mac_rfifo_empty_info;   //信息FIFO读空信号

  57. reg     [7:0]       mac_rdata_r1, mac_rdata_r2, mac_rdata_r3, mac_rdata_r4;//打拍
  58. reg                 mac_rvalid_r1, mac_rvalid_r2, mac_rvalid_r3, mac_rvalid_r4;//打拍

  59. localparam  WAIT_SFD            =   3'd0;
  60. localparam  CHECK_MAC_HEADER    =   3'd1;
  61. localparam  WRITE_FIFO          =   3'd2;
  62. localparam  RECORD_FRAME_LENGTH =   3'd3;
  63. localparam  WAIT_FRAME_END      =   3'd4;//STATE

  64. localparam  WAIT_MAC_FRAME          =   2'd0;
  65. localparam  READ_MAC_FRAME_DATA_LENGTH  =   2'd1;
  66. localparam  READ_MAC_FRAME_DATA     =   2'd2;//S_RFIFO

  67. localparam  ARP_TYPE    =   16'h0806;
  68. localparam  IP_TYPE     =   16'h0800;
  69. localparam  MAC_CONTROL_TYPE    =   16'h8808;
复制代码
由于用户接口时钟和PHY芯片接收时钟不同源,因此需要对输入的数据进行跨时钟域处理,再将数据传至上层。由于需要传输的数据量比较大,且用FIFO做跨时钟域比较简单,所以程序中使用异步FIFO做跨时钟域处理。    在uimac_rx和uimac_tx发送模块中,都使用了两个FIFO做跨时钟域处理,一个FIFO用来缓存数据,另一个FIFO用来缓存需要传递的信息,在一帧输入接收完成后,将信息写入帧信息FIFO,通过FIFO空标志信号来控制一帧数据读出。
信号打4拍再写入数据FIFO是为了过滤掉4字节CRC校验位。
  1. assign  O_mac_pause_addr    =   src_mac_addr;

  2. always@(posedge I_gmii_rclk) begin
  3.     mac_rdata_r1    <=  I_gmii_rdata;
  4.     mac_rdata_r2    <=  mac_rdata_r1;   
  5.     mac_rdata_r3    <=  mac_rdata_r2;
  6.     mac_rdata_r4    <=  mac_rdata_r3;
  7. end

  8. always@(posedge I_gmii_rclk) begin
  9.     mac_rvalid_r1   <=  I_gmii_rvalid;
  10.     mac_rvalid_r2   <=  mac_rvalid_r1;  
  11.     mac_rvalid_r3   <=  mac_rvalid_r2;
  12.     mac_rvalid_r4   <=  mac_rvalid_r3;
  13. end//打4拍,方便fifo只写入有效数据,而不写入crc校验位

  14. mac_rx_data_fifo mac_rx_data_fifo (
  15.     .rst            (I_reset),
  16.     .wr_clk         (I_gmii_rclk),
  17.     .din            (mac_rdata_r4),
  18.     .wr_en          (mac_wfifo_en & I_gmii_rvalid),//mac_wfifo_en控制只写入有效数据部分,I_gmii_rvalid控制最后的CRC部分不写入

  19.     .rd_clk         (I_mac_rclk),
  20.     .rd_en          (mac_rfifo_en),
  21.     .dout           (mac_rdata),
  22.     .full           (),
  23.     .empty          (),
  24.     .rd_data_count  (),
  25.     .wr_data_count  ()
  26. );

  27. mac_rx_frame_fifo mac_rx_frame_fifo (
  28.     .rst            (I_reset),
  29.     .wr_clk         (I_gmii_rclk),
  30.     .din            ({mac_wfifo_data_cnt_info,mac_frame_type}),
  31.     .wr_en          (mac_wfifo_en_info),

  32.     .rd_clk         (I_mac_rclk),
  33.     .rd_en          (mac_rfifo_en_info),
  34.     .dout           (mac_rfifo_data_info),
  35.     .full           (),
  36.     .empty          (mac_rfifo_empty_info)
  37. );

  38. crc32_check crc32_check
  39. (
  40.     .reset          (I_reset),
  41.     .clk            (I_gmii_rclk),
  42.     .CRC32_en       (crc_en & I_crc32_en),
  43.     .CRC32_init     (~mac_rvalid_r4),
  44.     .data           (mac_rdata_r4),
  45.     .CRC_data       (crc_data_out)
  46. );

  47. //mac帧控制,当接收方来不及处理接收数据,需要进行帧控制,通知发送模块。
  48. uimac_tx_frame_ctrl mac_tx_frame_ctrl
  49. (
  50.     .I_clk              (I_gmii_rclk),
  51.     .I_reset            (I_reset),
  52.     .I_mac_pause_en     (mac_rvalid_r4 & mac_pause_en),
  53.     .I_mac_data         (mac_rdata_r4),
  54.     .O_mac_pause_en     (O_mac_pause_en),
  55.     .O_mac_pause_time   (O_mac_pause_time)
  56. );
复制代码
通过状态机分别对FIFO写入数据和FIFO读出数据的时序进行控制。FIFO写入数据的状态机转换图如图所示。
2504661-20240809185149608-669920498.jpg
WAIT_SFD:初始状态为WAIT_SFD,等待接收到帧起始定界符8'hd5时,跳转到CHECK_MAC_HEADER帧头接收状态。
CHECK_MAC_HEADER:通过计数器将每一字节的帧头信息缓存,帧头信息接收完成后,判断接收到数据包的类型。如果接收到的MAC帧类型为IP包或者ARP包,跳转至WRITE_FIFO状态,并将数据FIFO写使能拉高,将有效数据写入数据FIFO,如果接收到的MAC帧类型为流量控制帧,则将有效数据传入帧流控制模块,对信息进行解析,跳转至FRAME_END状态,状态如果以上情况都不是,丢弃该帧,跳转至FRAME_END状态。
WRITE_FIFO:当数据有效信号为高时,将有效数据写入写数据FIFO,使用I_gmii_rvalid做为计数器计数的有效信号,是为了统计到有效数据的准确长度。CRC校验完成后,将帧长度和帧类型写入帧信息FIFO中,进入RECORD_FRAME_LENGTH状态。
RECORD_FRAME_LENGTH:帧信息写入帧信息FIFO完成后,回到WAIT_SFD状态,等待接收下一帧。
WAIT_FRAME_END:等待不传入上层的帧结束,回到WAIT_SFD状态,等待接收下一帧。
  1. always@(posedge I_gmii_rclk or posedge I_reset) begin
  2.     if(I_reset) begin
  3.         dst_mac_addr        <=  48'd0;
  4.         src_mac_addr        <=  48'd0;
  5.         mac_frame_type      <=  16'd0;
  6.         mac_wfifo_en        <=  1'b0;
  7.         mac_wfifo_en_info   <=  1'b0;
  8.         mac_wfifo_data_cnt_info <=  11'd0;
  9.         cnt                 <=  4'd0;
  10.         crc_en              <=  1'b0;
  11.         crc_cnt             <=  3'd4;
  12.         mac_rdata_error     <=  1'b1;
  13.         mac_pause_en        <=  1'b0;
  14.         STATE               <=  WAIT_SFD;
  15.     end
  16.     else begin
  17.         case(STATE)
  18.             WAIT_SFD:begin
  19.                 if(mac_rvalid_r4 & (mac_rdata_r4 == 8'hd5)) begin//以太网帧开始同步,一个字节为mac字段
  20.                     crc_en  <=  1'b1;//使能crc
  21.                     STATE   <=  CHECK_MAC_HEADER;//进入帧头接收
  22.                 end
  23.                 else
  24.                     STATE   <=  WAIT_SFD;
  25.             end
  26.             CHECK_MAC_HEADER:begin
  27.                 case(cnt)
  28.                     4'd0:begin  dst_mac_addr[47:40]     <=  mac_rdata_r4; cnt   <=  cnt + 1'b1; end
  29.                     4'd1:begin  dst_mac_addr[39:32]     <=  mac_rdata_r4; cnt   <=  cnt + 1'b1; end
  30.                     4'd2:begin  dst_mac_addr[31:24]     <=  mac_rdata_r4; cnt   <=  cnt + 1'b1; end
  31.                     4'd3:begin  dst_mac_addr[23:16]     <=  mac_rdata_r4; cnt   <=  cnt + 1'b1; end
  32.                     4'd4:begin  dst_mac_addr[15: 8]     <=  mac_rdata_r4; cnt   <=  cnt + 1'b1; end
  33.                     4'd5:begin  dst_mac_addr[ 7: 0]     <=  mac_rdata_r4; cnt   <=  cnt + 1'b1; end//目的mac
  34.                     4'd6:begin  src_mac_addr[47:40]     <=  mac_rdata_r4; cnt   <=  cnt + 1'b1; end
  35.                     4'd7:begin  src_mac_addr[39:32]     <=  mac_rdata_r4; cnt   <=  cnt + 1'b1; end
  36.                     4'd8:begin  src_mac_addr[31:24]     <=  mac_rdata_r4; cnt   <=  cnt + 1'b1; end
  37.                     4'd9:begin  src_mac_addr[23:16]     <=  mac_rdata_r4; cnt   <=  cnt + 1'b1; end
  38.                     4'd10:begin src_mac_addr[15: 8]     <=  mac_rdata_r4; cnt   <=  cnt + 1'b1; end
  39.                     4'd11:begin src_mac_addr[ 7: 0]     <=  mac_rdata_r4; cnt   <=  cnt + 1'b1; end
  40.                     4'd12:begin mac_frame_type[15: 8]   <=  mac_rdata_r4; cnt   <=  cnt + 1'b1; end//源mac
  41.                     4'd13:begin
  42.                         mac_frame_type[7:0] <=  mac_rdata_r4;
  43.                         cnt <=  4'd0;
  44.                         if(dst_mac_addr == I_mac_local_addr) begin//判断mac是否一致
  45.                             if({mac_frame_type[15:8], mac_rdata_r4} == IP_TYPE || {mac_frame_type[15:8], mac_rdata_r4} == ARP_TYPE) begin
  46.                                 mac_wfifo_en    <=  1'b1;//写fifo使能,只写入数据有效部分
  47.                                 STATE           <=  WRITE_FIFO;
  48.                             end
  49.                             else begin//需要过滤的帧
  50.                                 mac_wfifo_en    <=  1'b0;//禁止写fifo
  51.                                 STATE           <=  WAIT_FRAME_END;//过滤该帧,等待帧结束
  52.                             end
  53.                         end
  54.                         else if(dst_mac_addr == 48'h01_80_c2_00_00_01) begin//如果目的地址为48'h0180c2000001(固定值),mac控制帧,需要进行PAUSE流控制
  55.                             mac_wfifo_en    <=  1'b0;
  56.                             STATE           <=  WAIT_FRAME_END;
  57.                             if({mac_frame_type[15:8], mac_rdata_r4} == MAC_CONTROL_TYPE)//报文类型字段,需要进行pause流控制
  58.                                 mac_pause_en    <=  1'b1;//mac控制帧有效
  59.                             else
  60.                                 mac_pause_en    <=  1'b0;
  61.                         end
  62.                         else if(dst_mac_addr == 48'hff_ff_ff_ff_ff_ff) begin//对于广播地址,只接收arp包,其余类型的广播包全部过滤
  63.                             if({mac_frame_type[15:8], mac_rdata_r4} == ARP_TYPE) begin
  64.                                 mac_wfifo_en    <=  1'b1;//写帧数据fifo使能,只写入有效数据部分
  65.                                 STATE           <=  WRITE_FIFO;
  66.                             end
  67.                             else begin//需要过滤的帧
  68.                                 mac_wfifo_en    <=  1'b0;
  69.                                 STATE           <=  WAIT_FRAME_END;
  70.                             end
  71.                         end
  72.                         else begin//需要过滤的帧
  73.                             mac_wfifo_en    <=  1'b0;
  74.                             STATE           <=  WAIT_FRAME_END;
  75.                         end
  76.                     end
  77.                 endcase
  78.             end
  79.             WRITE_FIFO:begin//将去除首部后的ip数据包或者arp帧存入mac_rx_frame_fifo中,同时对当前数据包的长度进行统计
  80.                 if(I_gmii_rvalid) begin//写帧信息fifo
  81.                     mac_wfifo_data_cnt_info     <=  mac_wfifo_data_cnt_info + 1'b1;//有效数据计数器
  82.                     STATE                       <=  WRITE_FIFO;
  83.                 end
  84.                 else begin
  85.                     if(crc_cnt == 3'd0) begin//crc校验
  86.                         if(crc_data_out != 32'hc704dd7b)
  87.                             mac_rdata_error <=  1'b1;//校验正确
  88.                         else
  89.                             mac_rdata_error <=  1'b0;//校验错误

  90.                         mac_wfifo_en        <=  1'b0;
  91.                         mac_wfifo_en_info   <=  1'b1;//写帧信息fifo使能
  92.                         crc_en              <=  1'b0;
  93.                         crc_cnt             <=  3'd4;
  94.                         STATE               <=  RECORD_FRAME_LENGTH;//写入帧信息到帧信息fifo
  95.                     end
  96.                     else
  97.                         crc_cnt <=  crc_cnt - 1'b1;//crc计算计数器
  98.                 end
  99.             end
  100.             RECORD_FRAME_LENGTH:begin//写帧信息完成后,回到状态机WAIT_SFD
  101.                 mac_wfifo_en_info       <=  1'b0;
  102.                 mac_wfifo_data_cnt_info <=  11'd0;
  103.                 STATE                   <=  WAIT_SFD;//回到帧探测状态机
  104.             end
  105.             WAIT_FRAME_END:begin//等待帧结束
  106.                 if(mac_rvalid_r4)
  107.                     STATE               <=  WAIT_FRAME_END;
  108.                 else begin
  109.                     crc_en              <=  1'b0;
  110.                     mac_pause_en        <=  1'b0;
  111.                     STATE               <=  WAIT_SFD;
  112.                 end
  113.             end
  114.         endcase
  115.     end
  116. end
复制代码
FIFO读出数据的状态机转换图如图所示。
2504661-20240809185149993-120256250.jpg
WAIT_MAC_FRAME:当帧信息FIFO的空信号拉低,表明一帧数据已经接收完毕,将帧信息FIFO缓存的帧信息读出,并跳转至READ_MAC_FRAME_DATA_LENGTH状态。
READ_MAC_FRAME_DATA_LENGTH:该状态下开始读出数据FIFO缓存的有效数据,并将数据有效信号拉高,开始向上层传输数据包,随即进入READ_MAC_FRAME_DATA状态。
READN_MAC_FRAME_DATA:对读出的数据数量计数,当读出的数据量为帧信息FIFO中读出的帧长度时,停止读出数据,返回WAIT_MAC_FRAME状态。
  1. always@(posedge I_mac_rclk or posedge I_reset) begin
  2.     if(I_reset) begin
  3.         mac_rfifo_en_info   <=  1'b0;
  4.         mac_rdata_len       <=  11'd0;
  5.         mac_rdata_cnt       <=  11'd0;
  6.         mac_rfifo_en        <=  1'b0;
  7.         mac_rdata_type      <=  16'd0;
  8.         mac_rdata_valid     <=  1'b0;
  9.         S_RFIFO             <=  WAIT_MAC_FRAME;
  10.     end
  11.     else begin
  12.         case(S_RFIFO)
  13.             WAIT_MAC_FRAME:begin
  14.                 if(!mac_rfifo_empty_info) begin//接收mac帧信息fifo非空
  15.                     mac_rfifo_en_info   <=  1'b1;
  16.                     S_RFIFO             <=  READ_MAC_FRAME_DATA_LENGTH;
  17.                 end
  18.                 else
  19.                     S_RFIFO             <=  WAIT_MAC_FRAME;
  20.             end
  21.             READ_MAC_FRAME_DATA_LENGTH:begin
  22.                 mac_rdata_len       <=  mac_rfifo_data_info[26:16];//mac帧长度
  23.                 mac_rdata_type      <=  mac_rfifo_data_info[15:0];//mac类型
  24.                 mac_rfifo_en_info   <=  1'b0;
  25.                 mac_rfifo_en        <=  1'b1;//读数据fifo
  26.                 mac_rdata_valid     <=  1'b1;//数据有效
  27.                 S_RFIFO             <=  READ_MAC_FRAME_DATA;
  28.             end
  29.             READ_MAC_FRAME_DATA:begin
  30.                 if(mac_rdata_cnt < (mac_rdata_len - 1'b1)) begin//读完一帧数据
  31.                     mac_rdata_cnt   <=  mac_rdata_cnt + 1'b1;
  32.                     S_RFIFO         <=  READ_MAC_FRAME_DATA;
  33.                 end
  34.                 else begin
  35.                     mac_rfifo_en    <=  1'b0;
  36.                     mac_rdata_valid <=  1'b0;
  37.                     mac_rdata_cnt   <=  11'd0;
  38.                     mac_rdata_len   <=  11'd0;
  39.                     mac_rdata_type  <=  16'd0;
  40.                     S_RFIFO         <=  WAIT_MAC_FRAME;
  41.                 end
  42.             end
  43.         endcase
  44.     end
  45. end
复制代码
3.1.2 MAC发送模块
MAC发送模块主要实现以下功能:
(1)接收IP或ARP数据包,添加MAC帧首部,并对长度不足64字节的包进行补0。
(2)通过CRC校验模块,生成CRC校验值添加在帧的末尾。
(3)通过流控模块,接收MAC接收模块发送的暂停信号,进行流量控制。
(4)通过FIFO完成PHY发送时钟和用户接口时钟之间的时钟域的转换,并将数据输出至外部PHY芯片。
该模块数据跨时钟域转换的方式和uimac_rx模块类似,使用两个FIFO对数据进行处理。通过DR1_LOGIC_SHIFTER原语将数据延时,将帧头插入,该方法在uiip_tx和uiudp_tx模块中也有所体现,该原语可在TD安装路径下的arch文件夹的dr1_macro.v文件中找到。
2504661-20240809185151461-1992041387.jpg
控制FIFO写入数据的状态机跳转图如所示。
2504661-20240809185151849-50803231.jpg

WAIT_DATA_PACKET:当数据发送过来后,开始接收数据并缓存进FIFO中,并通过寄存器缓存帧信息,进入WIRTE_FIFO状态。如果数据FIFO写计数器大于阈值(本工程设置为2500,具体数值根据FIFO深度而定),则停止接收该帧,保持原状态不变。
WRITE_FIFO:写数据到数据FIFO中,并且对写入的数据长度进行计数,如果数据长度小于46,对数据末尾进行补0,一帧数据写入完成后,将长度、类型和地址信息写入帧信息FIFO,进入RECORD_DATA_PACKET_INFO状态。
RECORD_DATA_PACKET_INFO:信息写入帧信息FIFO完成后,回到WAIT_DATA_PACKET状态,等待接收下一帧数据。
  1. always@(posedge I_mac_tclk or posedge rst) begin
  2.     if(rst) begin
  3.         mac_wfifo_en_info           <=  1'b0;   //MAC消息FIFO,把MAC的信息包括,目的MAC地址、有效数据长度、帧类型写入到info fifo暂存
  4.         mac_wfifo_data_addr_info    <=  48'd0;  //MAC目的地址,暂存info fifo
  5.         mac_wfifo_data_type_info    <=  16'd0;  //MAC帧类型,暂存info fifo
  6.         mac_wfifo_data_cnt_info     <=  11'd0;  //MAC数据部分发送字节计数器
  7.         mac_wfifo_en                <=  1'b0;   //将帧数据写入到mac_tx_data_fifo缓存
  8.         mac_wfifo_data              <=  8'd0;   //将帧数据写入到mac_tx_data_fifo缓存
  9.         O_mac_tbusy                 <=  1'b1;   //通知外部模块,非忙
  10.         S_WFIFO                     <=  WAIT_DATA_PACKET;
  11.     end
  12.     else begin
  13.         case(S_WFIFO)
  14.             WAIT_DATA_PACKET:begin
  15.                 if(mac_wfifo_data_cnt > SEND_PAUSE_THRESHOLD) begin//当FIFO写通道数据计数器大于SEND_PAUSE_THRESHOLD,不进行新的一帧传输,O_mac_tbusy为握手信号,不进行握手(拉高)
  16.                     O_mac_tbusy                 <=  1'b0;
  17.                     S_WFIFO                     <=  WAIT_DATA_PACKET;
  18.                 end
  19.                 else begin
  20.                     if(I_mac_tvalid) begin//当有效数据发送过来后开始接收数据并且缓存到FIFO
  21.                         O_mac_tbusy             <=  1'b1;               //uimac_tx 忙
  22.                         mac_wfifo_en            <=  1'b1;               //将数据写入FIFO
  23.                         mac_wfifo_data          <=  I_mac_tdata;        //写入FIFO的数据
  24.                         mac_wfifo_data_addr_info<=  I_mac_tdest_addr;   //目的MAC地址
  25.                         mac_wfifo_data_type_info<=  {14'd0, I_mac_tdata_type};//数据类型
  26.                         mac_wfifo_data_cnt_info <=  mac_wfifo_data_cnt_info + 1'b1;//一帧数据的长度,以BYTE为单位
  27.                         S_WFIFO                 <=  WRITE_FIFO;//进入下一个状态等待写FIFO
  28.                     end
  29.                     else begin
  30.                         O_mac_tbusy             <=  1'b0;           //uimac_tx 非忙
  31.                         S_WFIFO                 <=  WAIT_DATA_PACKET;
  32.                     end
  33.                 end
  34.             end
  35.             WRITE_FIFO:begin//写数据到FIFO该FIFO用于缓存udp协议发送过来的数据
  36.                 if(I_mac_tvalid) begin//一帧数据接收过程中O_gmii_tdata_valid始终为高电平
  37.                     mac_wfifo_en                <=  1'b1;//继续写FIFO
  38.                     mac_wfifo_data              <=  I_mac_tdata;//写入FIFO的数据
  39.                     mac_wfifo_data_cnt_info     <=  mac_wfifo_data_cnt_info + 1'b1;//帧字节计数器累加
  40.                     S_WFIFO                     <=  WRITE_FIFO;
  41.                 end
  42.                 else begin
  43.                     if(mac_wfifo_data_cnt_info < 11'd46) begin//当一包/帧数据的长度小于46字节,自动补0(一帧数据最小64bytes,其中数据部分最小46bytes)
  44.                         mac_wfifo_en            <=  1'b1;
  45.                         mac_wfifo_data          <=  8'd0;
  46.                         mac_wfifo_en_info       <=  1'b0;
  47.                         mac_wfifo_data_cnt_info <=  mac_wfifo_data_cnt_info + 1'b1;
  48.                         S_WFIFO                 <=  WRITE_FIFO;
  49.                     end
  50.                     else begin//当一包/帧数据接收完,写包/帧信息 到包/帧信息FIFO
  51.                         mac_wfifo_en            <=  1'b0;
  52.                         mac_wfifo_data          <=  8'd0;
  53.                         mac_wfifo_en_info       <=  1'b1;
  54.                         S_WFIFO                 <=  RECORD_DATA_PACKET_INFO;
  55.                     end
  56.                 end
  57.             end
  58.             RECORD_DATA_PACKET_INFO:begin//时序中,该周期完成写包/帧信息 到包/帧信息FIFO
  59.                 mac_wfifo_en_info               <=  1'b0;
  60.                 mac_wfifo_data_addr_info        <=  48'd0;
  61.                 mac_wfifo_data_type_info        <=  16'd0;
  62.                 mac_wfifo_data_cnt_info         <=  11'd0;
  63.                 S_WFIFO                         <=  WAIT_DATA_PACKET;
  64.             end
  65.         endcase
  66.     end
  67. end
复制代码
控制FIFO读出数据的状态机转换图如图所示。
2504661-20240809185152211-1528858704.jpg

WAIT_DATA_PACKET:帧信息FIFO非空,说明有帧需要发送,将帧信息FIFO读使能拉高一个时钟周期,跳转至READ_DATA_PACKET_INFO状态。
READ_DATA_PACKET_INFO:对读出的帧信息进行解析,通过帧类型得到目地MAC地址,并拉高数据FIFO读使能,开始读出有效数据,输入进移位寄存器中,进入RAED_DATA_PACKET状态。如果PAUSE标志信号为高,且PAUSE帧的地址和目的MAC地址相同,说明对方请求暂停发送,则进入WAIT_PAUSE_END状态。
READ_DATA_PACKET:当一帧数据读完后,进入WAIT_CRC_TRANS_DONE状态。
WAIT_CRC_TRANS_DONE:等待CRC计数器置零,代表一帧数据发送完成,进入ADD_IFG状态。
ADD_IFG:等待最小帧间隔结束,回到WAIT_DATA_PACKET状态,等待接收下一帧。
WAIT_PAUSE_END:等待PAUSE标志信号拉低,暂停结束,此时将数据FIFO读使能拉高,继续读该帧的有效数据,进入READ_DATA_PACKET状态。
READ_DATA_PACKET:将shift_ram移位后的数据进行组帧,通过计数器添加前导码、SFD、目的MAC、源MAC和类型字段,并在帧末尾填充4字节CRC校验数据。
  1. //完成MAC帧的发送,用到了前面的帧缓存FIFO,信息缓存FIFO,以及SHIFT寄存器(实现MAC帧头信息插入)
  2. always@(posedge I_gmii_tclk or posedge I_reset) begin
  3.     if(I_reset) begin
  4.         mac_rfifo_data_en           <=  1'b0;   
  5.         mac_rfifo_en_info           <=  1'b0;   
  6.         mac_rfifo_data_cnt          <=  11'd0;  
  7.         mac_rfifo_data_length       <=  11'd0;  
  8.         mac_rfifo_data_addr         <=  48'd0;  
  9.         ether_type                  <=  16'd0;  
  10.         inter_gap_cnt               <=  4'd0;   
  11.         S_RFIFO                     <=  WAIT_DATA_PACKET;
  12.     end
  13.     else begin
  14.         case(S_RFIFO)
  15.             WAIT_DATA_PACKET:begin
  16.                 if(!mac_rfifo_empty_info) begin//帧信息FIFO非空代表有帧需要发送
  17.                     mac_rfifo_en_info       <=  1'b1;//FIFO是设置的FWT模式,如果只有1帧数据,那么FIFO被读空,否则FIFO输出更新到下一帧
  18.                     S_RFIFO                 <=  READ_DATA_PACKET_INFO;
  19.                 end
  20.                 else
  21.                     S_RFIFO                 <=  WAIT_DATA_PACKET;
  22.             end
  23.             READ_DATA_PACKET_INFO:begin
  24.                 if(mac_rfifo_data_info[15:0] == 16'h0002) begin//发送的ARP包类型为应答包
  25.                     ether_type              <=  ARP_PACKET;
  26.                     mac_rfifo_data_addr     <=  mac_rfifo_data_info[74:27];//MAC地址
  27.                 end
  28.                 else if(mac_rfifo_data_info[15:0] == 16'h0003) begin
  29.                     ether_type              <=  ARP_PACKET;
  30.                     mac_rfifo_data_addr     <=  48'hff_ff_ff_ff_ff_ff;//广播地址
  31.                 end
  32.                 else begin
  33.                     ether_type              <=  IP_PACKET;//IP 包
  34.                     mac_rfifo_data_addr     <=  mac_rfifo_data_info[74:27];//MAC地址
  35.                 end

  36.                 mac_rfifo_data_length       <=  mac_rfifo_data_info[26:16];//数据长度
  37.                 mac_rfifo_en_info           <=  1'b0;

  38.                 if(pause_flag && mac_rfifo_data_info[74:27] == pause_dst_mac_addr) begin//如果存在PAUSE帧需要发送,并且目的地址和当前目的地址一致
  39.                     mac_rfifo_data_en       <=  1'b0;//PAUSE 帧阶段不从FIFO读数据   
  40.                     S_RFIFO                 <=  WAIT_PAUSE_END;//等待PAUSE流控制结束
  41.                 end
  42.                 else begin
  43.                     mac_rfifo_data_en       <=  1'b1;
  44.                     S_RFIFO                 <=  READ_DATA_PACKET;
  45.                 end
  46.             end
  47.             READ_DATA_PACKET:begin
  48.                 if(mac_rfifo_data_cnt == (mac_rfifo_data_length - 1'b1)) begin//一帧数据从FIFO读完(上一个状态已经读出了一个周期,所以这里少计一个数)
  49.                     mac_rfifo_data_en       <=  1'b0;
  50.                     mac_rfifo_data_length   <=  11'd0;
  51.                     mac_rfifo_data_cnt      <=  11'd0;
  52.                     mac_rfifo_data_addr     <=  48'd0;
  53.                     ether_type              <=  16'd0;
  54.                     S_RFIFO                 <=  WAIT_CRC_TRANS_DONE;   
  55.                 end
  56.                 else begin
  57.                     mac_rfifo_data_en       <=  1'b1;
  58.                     mac_rfifo_data_cnt      <=  mac_rfifo_data_cnt + 1'b1;
  59.                     S_RFIFO                 <=  READ_DATA_PACKET;
  60.                 end
  61.             end
  62.             WAIT_CRC_TRANS_DONE:begin//等待正在发送的MAC数据包CRC发送完成
  63.                 if(crc_cnt)
  64.                     S_RFIFO                 <=  WAIT_CRC_TRANS_DONE;
  65.                 else
  66.                     S_RFIFO                 <=  ADD_IFG;
  67.             end
  68.             ADD_IFG:begin//数据包发送后,插入帧间隔,2帧之间最少需要IFGmini=96bit/speed,比如1000M 96ns 100M 960ns 10M 9600ns
  69.                 if(inter_gap_cnt == (IFG - 4'd4)) begin//插入最小帧间隔周期,在此状态机,MAC_SEND_FLOW_CONTROL 流控制模可以发送PAUSE帧,减去4'd4是本计数器结束后,距离下一帧发送实际需要还要经过4个时钟周期
  70.                     inter_gap_cnt           <=  4'd0;
  71.                     S_RFIFO                 <=  WAIT_DATA_PACKET;//进入WAIT_DATA_PACKET
  72.                 end
  73.                 else begin
  74.                     inter_gap_cnt           <=  inter_gap_cnt + 1'b1;
  75.                     S_RFIFO                 <=  ADD_IFG;
  76.                 end
  77.             end
  78.             WAIT_PAUSE_END:begin//等待暂停结束后重新传输数据
  79.                 if(pause_flag) begin//pause 控制
  80.                     mac_rfifo_data_en       <=  1'b0;
  81.                     S_RFIFO                 <=  WAIT_PAUSE_END;
  82.                 end
  83.                 else begin
  84.                     mac_rfifo_data_en       <=  1'b1;//暂停结束后,继续读帧FIFO中数据
  85.                     S_RFIFO                 <=  READ_DATA_PACKET;
  86.                 end
  87.             end
  88.         endcase
  89.     end
  90. end

  91. always@(posedge I_gmii_tclk or posedge I_reset) begin
  92.     if(I_reset) begin
  93.         O_gmii_tvalid       <=  1'b0;
  94.         mac_tdata           <=  8'd0;
  95.         mac_tdata_crc_en    <=  1'b0;
  96.         data22_cnt          <=  5'd0;
  97.         data22_shift_cnt    <=  5'd22;
  98.         crc_cnt             <=  3'd4;
  99.         crc_read            <=  1'b0;
  100.     end
  101.     else if(mac_rfifo_data_en) begin
  102.         case(data22_cnt)//这个阶段移位寄存器进行数据的填充
  103.             0   :begin  mac_tdata   <=  8'h55;    data22_cnt  <=  data22_cnt + 1'b1; O_gmii_tvalid    <=  1'b1; data22_shift_cnt  <=  5'd22;end
  104.             1   :begin  mac_tdata   <=  8'h55;    data22_cnt  <=  data22_cnt + 1'b1; end
  105.             2   :begin  mac_tdata   <=  8'h55;    data22_cnt  <=  data22_cnt + 1'b1; end  
  106.             3   :begin  mac_tdata   <=  8'h55;    data22_cnt  <=  data22_cnt + 1'b1; end
  107.             4   :begin  mac_tdata   <=  8'h55;    data22_cnt  <=  data22_cnt + 1'b1; end
  108.             5   :begin  mac_tdata   <=  8'h55;    data22_cnt  <=  data22_cnt + 1'b1; end
  109.             6   :begin  mac_tdata   <=  8'h55;    data22_cnt  <=  data22_cnt + 1'b1; end
  110.             7   :begin  mac_tdata   <=  8'hd5;    data22_cnt  <=  data22_cnt + 1'b1; end
  111.             
  112.             8   :begin  mac_tdata   <=  mac_rfifo_data_addr[47:40]; data22_cnt  <=  data22_cnt + 1'b1;
  113. mac_tdata_crc_en <=  1'b1; end
  114.             9   :begin  mac_tdata   <=  mac_rfifo_data_addr[39:32]; data22_cnt  <=  data22_cnt + 1'b1; end
  115.             10  :begin  mac_tdata   <=  mac_rfifo_data_addr[31:24]; data22_cnt  <=  data22_cnt + 1'b1; end
  116.             11  :begin  mac_tdata   <=  mac_rfifo_data_addr[23:16]; data22_cnt  <=  data22_cnt + 1'b1; end
  117.             12  :begin  mac_tdata   <=  mac_rfifo_data_addr[15:8];  data22_cnt  <=  data22_cnt + 1'b1; end
  118.             13  :begin  mac_tdata   <=  mac_rfifo_data_addr[7:0];   data22_cnt  <=  data22_cnt + 1'b1; end
  119.             14  :begin  mac_tdata   <=  I_mac_local_addr[47:40];    data22_cnt  <=  data22_cnt + 1'b1; end
  120.             15  :begin  mac_tdata   <=  I_mac_local_addr[39:32];    data22_cnt  <=  data22_cnt + 1'b1; end
  121.             16  :begin  mac_tdata   <=  I_mac_local_addr[31:24];    data22_cnt  <=  data22_cnt + 1'b1; end
  122.             17  :begin  mac_tdata   <=  I_mac_local_addr[23:16];    data22_cnt  <=  data22_cnt + 1'b1; end
  123.             18  :begin  mac_tdata   <=  I_mac_local_addr[15:8];     data22_cnt  <=  data22_cnt + 1'b1; end
  124.             19  :begin  mac_tdata   <=  I_mac_local_addr[7:0];      data22_cnt  <=  data22_cnt + 1'b1; end
  125.             
  126.             20  :begin  mac_tdata   <=  ether_type[15:8];           data22_cnt  <=  data22_cnt + 1'b1; end
  127.             21  :begin  mac_tdata   <=  ether_type[7:0];            data22_cnt  <=  data22_cnt + 1'b1; end
  128.             22  :begin  mac_tdata   <=  mac_tdata_shift_out; end
  129.             default:    data22_cnt  <=  5'd0;
  130.         endcase
  131.     end
  132.     else if(!mac_rfifo_data_en) begin//tmac_en=1阶段会读取mac_tx_frame_info_fifo中所有的数据写到移位寄存器,当tmac_en=0,移位寄存器剩余22个有效数据需要移除
  133.         if(data22_shift_cnt != 5'd0) begin//将移位寄存器组中的剩余22个数据读出
  134.             mac_tdata           <=  mac_tdata_shift_out;
  135.             data22_shift_cnt    <=  data22_shift_cnt - 1'b1;
  136.         end
  137.         else begin
  138.             if(I_crc32_en && O_gmii_tvalid) begin //开始传送帧的CRC32校验值
  139.                 O_gmii_tvalid       <=  1'b1;
  140.                 data22_cnt          <=  5'd0;
  141.                 mac_tdata_crc_en    <=  1'b0;//停止CRC计算
  142.                 crc_read            <=  1'b1;//开始传输CRC32校验值
  143.                 if(crc_cnt != 3'd0)
  144.                     crc_cnt         <=  crc_cnt - 1'b1;
  145.                 else begin
  146.                     O_gmii_tvalid   <=  1'b0;
  147.                     crc_read        <=  1'b0;//4字节的CRC校验值传输完毕
  148.                     crc_cnt         <=  3'd4;
  149.                 end
  150.             end
  151.             else begin//不进行CRC32校验,无需传输校验值
  152.                 O_gmii_tvalid       <=  1'b0;
  153.                 data22_shift_cnt    <=  5'd0;
  154.             end
  155.         end
  156.     end
  157. end
复制代码
3.1.3 CRC校验模块
通过网页生成CRC校验代码,将代码稍作修改,得到CRC校验计算模块。注意uimac_rx模块将接收到的CRC校验位也进行CRC校验,若校验计算结果为0则校验正确。
  1. `timescale 1ns / 1ps
  2. module crc32_gen(
  3. input   reset,
  4. input   clk,
  5. input   CRC32_en,         //CRC校验使能信号
  6. input   CRC32_init,       //CRC校验值初始化信号
  7. input   CRC_read,
  8.      //input   CRC32_valid,      //CRC校验值维持有效
  9. input  [7:0]  data,  
  10. output [7:0]  CRC_out
  11. );
  12. reg [31:0]   CRC_temp;
  13. assign CRC_out = CRC_read ? ~{CRC_temp[24], CRC_temp[25], CRC_temp[26], CRC_temp[27],
  14.                                CRC_temp[28], CRC_temp[29], CRC_temp[30], CRC_temp[31]} : 8'h00;           
  15.    
  16. always@(posedge clk or posedge reset)         
  17.    if(reset)
  18.         CRC_temp <= 32'hffffffff;         
  19.    else if(CRC32_init)
  20.         CRC_temp <= 32'hffffffff;
  21.    else if(CRC32_en)
  22.       begin
  23. CRC_temp[0]<=CRC_temp[24]^CRC_temp[30]^data[1]^data[7];
  24. CRC_temp[1]<=CRC_temp[25]^CRC_temp[31]^data[0]^data[6]^CRC_temp[24]^CRC_temp[30]^data[1]^data[7];
  25. CRC_temp[2]<=CRC_temp[26]^data[5]^CRC_temp[25]^CRC_temp[31]^data[0]^data[6]^CRC_temp[24]^CRC_temp[30]^data[1]
  26. ^data[7];
  27. CRC_temp[3]<=CRC_temp[27]^data[4]^CRC_temp[26]^data[5]^CRC_temp[25]^CRC_temp[31]^data[0]^data[6];
  28. CRC_temp[4]<=CRC_temp[28]^data[3]^CRC_temp[27]^data[4]^CRC_temp[26]^data[5]^CRC_temp[24]^CRC_temp[30]^data[1]
  29. ^data[7];
  30. CRC_temp[5]<=CRC_temp[29]^data[2]^CRC_temp[28]^data[3]^CRC_temp[27]^data[4]^CRC_temp[25]^CRC_temp[31]^data[0]^data[6]^CRC_temp[24]^CRC_temp[30]^data[1]^data[7];
  31. CRC_temp[6]<=CRC_temp[30]^data[1]^CRC_temp[29]^data[2]^CRC_temp[28]^data[3]^CRC_temp[26]^data[5]^CRC_temp[25]^CRC_temp[31]^data[0]^data[6];
  32. CRC_temp[7]<=CRC_temp[31]^data[0]^CRC_temp[29]^data[2]^CRC_temp[27]^data[4]^CRC_temp[26]^data[5]^CRC_temp[24]^data[7];
  33. CRC_temp[8]<=CRC_temp[0]^CRC_temp[28]^data[3]^CRC_temp[27]^data[4]^CRC_temp[25]^data[6]^CRC_temp[24]^data[7];
  34. CRC_temp[9]<=CRC_temp[1]^CRC_temp[29]^data[2]^CRC_temp[28]^data[3]^CRC_temp[26]^data[5]^CRC_temp[25]^data[6];
  35. CRC_temp[10]<=CRC_temp[2]^CRC_temp[29]^data[2]^CRC_temp[27]^data[4]^CRC_temp[26]^data[5]^CRC_temp[24]^data[7];
  36. CRC_temp[11]<=CRC_temp[3]^CRC_temp[28]^data[3]^CRC_temp[27]^data[4]^CRC_temp[25]^data[6]^CRC_temp[24]^data[7];
  37. CRC_temp[12]<=CRC_temp[4]^CRC_temp[29]^data[2]^CRC_temp[28]^data[3]^CRC_temp[26]^data[5]^CRC_temp[25]^data[6]^CRC_temp[24]^CRC_temp[30]^data[1]^data[7];
  38. CRC_temp[13]<=CRC_temp[5]^CRC_temp[30]^data[1]^CRC_temp[29]^data[2]^CRC_temp[27]^data[4]^CRC_temp[26]^data[5]^CRC_temp[25]^CRC_temp[31]^data[0]^data[6];
  39. CRC_temp[14]<=CRC_temp[6]^CRC_temp[31]^data[0]^CRC_temp[30]^data[1]^CRC_temp[28]^data[3]^CRC_temp[27]^data[4]^CRC_temp[26]^data[5];
  40. CRC_temp[15]<=CRC_temp[7]^CRC_temp[31]^data[0]^CRC_temp[29]^data[2]^CRC_temp[28]^data[3]^CRC_temp[27]^data[4];
  41. CRC_temp[16]<=CRC_temp[8]^CRC_temp[29]^data[2]^CRC_temp[28]^data[3]^CRC_temp[24]^data[7];
  42. CRC_temp[17]<=CRC_temp[9]^CRC_temp[30]^data[1]^CRC_temp[29]^data[2]^CRC_temp[25]^data[6];
  43. CRC_temp[18]<=CRC_temp[10]^CRC_temp[31]^data[0]^CRC_temp[30]^data[1]^CRC_temp[26]^data[5];
  44. CRC_temp[19]<=CRC_temp[11]^CRC_temp[31]^data[0]^CRC_temp[27]^data[4];
  45. CRC_temp[20]<=CRC_temp[12]^CRC_temp[28]^data[3];
  46. CRC_temp[21]<=CRC_temp[13]^CRC_temp[29]^data[2];
  47. CRC_temp[22]<=CRC_temp[14]^CRC_temp[24]^data[7];
  48. CRC_temp[23]<=CRC_temp[15]^CRC_temp[25]^data[6]^CRC_temp[24]^CRC_temp[30]^data[1]^data[7];
  49. CRC_temp[24]<=CRC_temp[16]^CRC_temp[26]^data[5]^CRC_temp[25]^CRC_temp[31]^data[0]^data[6];
  50. CRC_temp[25]<=CRC_temp[17]^CRC_temp[27]^data[4]^CRC_temp[26]^data[5];
  51. CRC_temp[26]<=CRC_temp[18]^CRC_temp[28]^data[3]^CRC_temp[27]^data[4]^CRC_temp[24]^CRC_temp[30]^data[1]^data[7];
  52. CRC_temp[27]<=CRC_temp[19]^CRC_temp[29]^data[2]^CRC_temp[28]^data[3]^CRC_temp[25]^CRC_temp[31]^data[0]^data[6];
  53. CRC_temp[28]<=CRC_temp[20]^CRC_temp[30]^data[1]^CRC_temp[29]^data[2]^CRC_temp[26]^data[5];
  54. CRC_temp[29]<=CRC_temp[21]^CRC_temp[31]^data[0]^CRC_temp[30]^data[1]^CRC_temp[27]^data[4];
  55. CRC_temp[30]<=CRC_temp[22]^CRC_temp[31]^data[0]^CRC_temp[28]^data[3];
  56. CRC_temp[31]<=CRC_temp[23]^CRC_temp[29]^data[2];
  57.       end
  58.     else if(CRC_read)
  59.         CRC_temp <= {CRC_temp[23:0], 8'hff};
  60.          
  61. endmodule
复制代码
3.1.4 PAUSE帧流控制模块
uimac_rx模块接收到PAUSE帧后,会将有效数据段送入mac_tx_frame_ctrl模块进行解析,得到暂停时间和PAUSE帧发送方的MAC地址,将其发送给uimac_tx模块的mac_tx_pause_ctrl子模块。
  1. /*******************************mac_tx_frame_ctrl模块*********************
  2. --以下是米联客设计的mac_tx_frame_ctrl模块,用于产生MAC发送模块的PAUSE暂停发送
  3. 1.
  4. *********************************************************************/
  5. `timescale 1ns/1ps
  6. module  uimac_tx_frame_ctrl
  7. (
  8.     input   wire                I_clk,
  9.     input   wire                I_reset,
  10.     input   wire                I_mac_pause_en,
  11.     input   wire    [7:0]       I_mac_data,
  12.     output  reg                 O_mac_pause_en,
  13.     output  reg     [21:0]      O_mac_pause_time//发送MAC停止发送数据的时间
  14. );

  15. reg     [15:0]              opcode;
  16. reg     [15:0]              pause_time;//pause_time字段为发送MAC停止发送数据的时间,每单位为512bit传输时间,比如数值为16’d1024表示暂停时间为MAC传输1024*512bit数据所需要的时间
  17. reg     [2:0]               cnt;
  18. reg                         STATE;

  19. localparam  READ_FRAME      =   0;
  20. localparam  WAIT_FRAME_END  =   1;

  21. localparam  PAUSE_FRAME     =   16'h0001;//操作码,固定值为0x0001

  22. always@(posedge I_clk or posedge I_reset) begin
  23.     if(I_reset) begin
  24.         cnt                 <=  3'd0;
  25.         opcode              <=  16'd0;
  26.         pause_time          <=  16'd0;
  27.         O_mac_pause_en      <=  1'b0;
  28.         O_mac_pause_time    <=  22'd0;
  29.         STATE               <=  READ_FRAME;
  30.     end
  31.     else begin
  32.         case(STATE)
  33.             READ_FRAME:begin
  34.                 if(I_mac_pause_en)//帧流控制有效
  35.                     case(cnt)
  36.                         0:begin opcode[15: 8]   <=  I_mac_data; cnt <=  cnt + 1'b1;end
  37.                         1:begin
  38.                             opcode[ 7: 0]   <=  I_mac_data;
  39.                             if({opcode[15: 8], I_mac_data} == PAUSE_FRAME) begin//判断是PAUSE帧
  40.                                 STATE   <=  READ_FRAME;
  41.                                 cnt     <=  cnt + 1'b1;
  42.                             end
  43.                             else begin
  44.                                 STATE   <=  WAIT_FRAME_END;
  45.                                 cnt     <=  3'd0;
  46.                             end
  47.                         end
  48.                         2:begin pause_time[15: 8]   <=  I_mac_data; cnt <=  cnt + 1'b1;end
  49.                         3:begin pause_time[ 7: 0]   <=  I_mac_data; cnt <=  cnt + 1'b1;end//需要暂停发送的时间
  50.                         4:begin
  51.                             cnt                 <=  3'd0;
  52.                             opcode              <=  16'd0;
  53.                             pause_time          <=  16'd0;
  54.                             O_mac_pause_en      <=  1'b1;//通知MAC发送控制器,接收到了PAUSE帧
  55.                             O_mac_pause_time    <=  {pause_time, 6'd0};//*512/8 = *64 = *(2^6)
  56.                             STATE               <=  WAIT_FRAME_END;//等待帧结束
  57.                         end
  58.                     endcase
  59.                 else
  60.                     STATE   <=  READ_FRAME;
  61.             end
  62.             WAIT_FRAME_END:begin//等待帧结束
  63.                 O_mac_pause_time    <=  22'd0;
  64.                 O_mac_pause_en      <=  1'b0;
  65.                 if(I_mac_pause_en)
  66.                     STATE   <=  WAIT_FRAME_END;
  67.                 else
  68.                     STATE   <=  READ_FRAME;
  69.             end
  70.         endcase
  71.     end
  72. end

  73. endmodule
复制代码
mac_tx_pause_ctrl接收到mac_pasue_en信号为高时,将接收的信息寄存,状态机跳转,等待MAC发送端发送完一帧数据,进入帧间隔等待。当MAC发送模块进入帧间隔状态后,流控模块拉高pause_flag信号,等待暂停时间结束后将信号拉低。
  1. /*******************************mac_tx_pause_ctrl模块*********************
  2. --以下是米联客设计的mac_tx_pause_ctrl MAC发送端,流控制器模块
  3. 1.
  4. *********************************************************************/
  5. `timescale 1ns/1ps
  6. module  uimac_tx_pause_ctrl
  7. (
  8.     input   wire                I_clk,
  9.     input   wire                I_reset,
  10.     input   wire    [2:0]       I_mac_state,
  11.     input   wire                I_mac_pause_en,
  12.     input   wire    [21:0]      I_mac_pause_time,
  13.     input   wire    [47:0]      I_mac_pause_addr,
  14.     output  reg     [47:0]      O_pause_dst_mac_addr,
  15.     output  reg                 O_pause_flag
  16. );

  17. reg     [21:0]          pause_clk_num;
  18. reg     [21:0]          pause_clk_cnt;
  19. reg     [1:0]           STATE;

  20. localparam  WAIT_PAUSE_FRAME        =   2'd0;
  21. localparam  WAIT_CURRENT_SEND_DONE  =   2'd1;
  22. localparam  MAC_SEND_PAUSE          =   2'd2;

  23. localparam  ADD_IFG     =   3'd4;

  24. always@(posedge I_clk or posedge I_reset) begin
  25.     if(I_reset) begin
  26.         pause_clk_num           <=  22'd0;
  27.         pause_clk_cnt           <=  22'd0;
  28.         O_pause_flag            <=  1'b0;
  29.         O_pause_dst_mac_addr    <=  48'd0;
  30.         STATE                   <=  WAIT_PAUSE_FRAME;
  31.     end
  32.     else begin
  33.         case(STATE)
  34.             WAIT_PAUSE_FRAME:begin//等待PAUSE帧
  35.                 O_pause_flag    <=  1'b0;
  36.                 if(I_mac_pause_en) begin    //MAC接收模块接收到PAUSE帧
  37.                     O_pause_dst_mac_addr    <=  I_mac_pause_addr;//MAC发送模块需要发送PAUSE的目的MAC地址        
  38.                     pause_clk_num           <=  I_mac_pause_time;//PAUSE时间,在MAC接收端已经换算好需要PAUSE的时钟周期个数
  39.                     STATE                   <=  WAIT_CURRENT_SEND_DONE;
  40.                 end
  41.                 else begin
  42.                     O_pause_dst_mac_addr    <=  48'd0;
  43.                     pause_clk_num           <=  22'd0;
  44.                     STATE                   <=  WAIT_PAUSE_FRAME;                  
  45.                 end
  46.             end
  47.             WAIT_CURRENT_SEND_DONE:begin//等待当MAC发送状态机在I_mac_state == ADD_IFG状态的时候,设置O_pause_flag标志
  48.                 if(I_mac_state == ADD_IFG) begin
  49.                     O_pause_flag            <=  1'b1;//设置O_pause_flag,通知MAC 帧发送模块,暂停数据发送
  50.                     STATE                   <=  MAC_SEND_PAUSE;
  51.                 end
  52.                 else begin
  53.                     O_pause_flag            <=  1'b0;
  54.                     STATE                   <=  WAIT_CURRENT_SEND_DONE;
  55.                 end
  56.             end
  57.             MAC_SEND_PAUSE:begin//暂停数据发送,等待(pause_clk_num - 3)个时钟周期
  58.                 if(pause_clk_cnt == (pause_clk_num - 3)) begin
  59.                     O_pause_flag            <=  1'b0;
  60.                     O_pause_dst_mac_addr    <=  48'd0;
  61.                     pause_clk_cnt           <=  22'd0;
  62.                     pause_clk_num           <=  22'd0;
  63.                     STATE                   <=  WAIT_PAUSE_FRAME;
  64.                 end
  65.                 else begin
  66.                     O_pause_flag            <=  1'b1;//设置O_pause_flag,通知MAC 帧发送模块,暂停数据发送
  67.                     pause_clk_cnt           <=  pause_clk_cnt + 1'b1;
  68.                     STATE                   <=  MAC_SEND_PAUSE;
  69.                 end
  70.             end
  71.         endcase
  72.     end
  73. end

  74. endmodule
复制代码
copycode.gif
copycode.gif
copycode.gif
copycode.gif
copycode.gif
copycode.gif
copycode.gif
copycode.gif
copycode.gif
copycode.gif
copycode.gif
copycode.gif
copycode.gif
copycode.gif
copycode.gif
copycode.gif
copycode.gif
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则