[米联客-安路飞龙DR1-FPSOC] UDP通信篇连载-02 MAC层程序设计
软件版本: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程序来实现以太网各个子层的功能。程序整体架构图如下:
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 31 wire [7:0] mac_rdata; 32 reg mac_rdata_valid; 33 reg [15:0] mac_rdata_type; 34 reg mac_rdata_error; 35 36 assign O_mac_rdata = mac_rdata; 37 assign O_mac_rvalid = mac_rdata_valid; 38 assign O_mac_rdata_type = mac_rdata_type; 39 assign O_mac_rdata_error = mac_rdata_error; 40 41 reg [10:0] mac_rdata_cnt; 42 reg mac_wfifo_en; //FIFO写入数据有效使能 43 reg mac_rfifo_en; //FIFO读数据使能 44 45 reg crc_en; //crc校验使能 46 reg [2:0] crc_cnt; //移位计数器 47 wire [31:0] crc_data_out; //crc校验结果输出 48 reg [2:0] STATE; //写FIFO状态机 49 reg [1:0] S_RFIFO; //读FIFO状态机 50 51 reg [47:0] dst_mac_addr; //MAC帧解析出的目的MAC地址(接收方的MAC地址) 52 reg [47:0] src_mac_addr; //MAC帧解析出的源MAC地址(发送方的MAC地址) 53 reg [15:0] mac_frame_type; //上层数据包类型(0x0800 ip;0x0806 arp;0x8808 mac_ctrl) 54 reg mac_pause_en; //PAUSE帧有效使能 55 reg [3:0] cnt; //对MAC帧头部的字节数计数 56 57 reg [10:0] mac_wfifo_data_cnt_info;//写帧信息字节数到信息FIFO 58 reg mac_wfifo_en_info; //写帧信息FIFO有效使能 59 reg mac_rfifo_en_info; //读帧信息FIFO有效使能 60 wire [26:0] mac_rfifo_data_info; //从信息FIFO读出帧信息 61 reg [10:0] mac_rdata_len; //mac帧长度 62 wire mac_rfifo_empty_info; //信息FIFO读空信号 63 64 reg [7:0] mac_rdata_r1, mac_rdata_r2, mac_rdata_r3, mac_rdata_r4;//打拍 65 reg mac_rvalid_r1, mac_rvalid_r2, mac_rvalid_r3, mac_rvalid_r4;//打拍 66 67 localparam WAIT_SFD = 3'd0; 68 localparam CHECK_MAC_HEADER = 3'd1; 69 localparam WRITE_FIFO = 3'd2; 70 localparam RECORD_FRAME_LENGTH = 3'd3; 71 localparam WAIT_FRAME_END = 3'd4;//STATE 72 73 localparam WAIT_MAC_FRAME = 2'd0; 74 localparam READ_MAC_FRAME_DATA_LENGTH = 2'd1; 75 localparam READ_MAC_FRAME_DATA = 2'd2;//S_RFIFO 76 77 localparam ARP_TYPE = 16'h0806; 78 localparam IP_TYPE = 16'h0800; 79 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 3 always@(posedge I_gmii_rclk) begin 4 mac_rdata_r1 <= I_gmii_rdata; 5 mac_rdata_r2 <= mac_rdata_r1; 6 mac_rdata_r3 <= mac_rdata_r2; 7 mac_rdata_r4 <= mac_rdata_r3; 8 end 9 10 always@(posedge I_gmii_rclk) begin 11 mac_rvalid_r1 <= I_gmii_rvalid; 12 mac_rvalid_r2 <= mac_rvalid_r1; 13 mac_rvalid_r3 <= mac_rvalid_r2; 14 mac_rvalid_r4 <= mac_rvalid_r3; 15 end//打4拍,方便fifo只写入有效数据,而不写入crc校验位 16 17 mac_rx_data_fifo mac_rx_data_fifo ( 18 .rst (I_reset), 19 .wr_clk (I_gmii_rclk), 20 .din (mac_rdata_r4), 21 .wr_en (mac_wfifo_en & I_gmii_rvalid),//mac_wfifo_en控制只写入有效数据部分,I_gmii_rvalid控制最后的CRC部分不写入 22 23 .rd_clk (I_mac_rclk), 24 .rd_en (mac_rfifo_en), 25 .dout (mac_rdata), 26 .full (), 27 .empty (), 28 .rd_data_count (), 29 .wr_data_count () 30 ); 31 32 mac_rx_frame_fifo mac_rx_frame_fifo ( 33 .rst (I_reset), 34 .wr_clk (I_gmii_rclk), 35 .din ({mac_wfifo_data_cnt_info,mac_frame_type}), 36 .wr_en (mac_wfifo_en_info), 37 38 .rd_clk (I_mac_rclk), 39 .rd_en (mac_rfifo_en_info), 40 .dout (mac_rfifo_data_info), 41 .full (), 42 .empty (mac_rfifo_empty_info) 43 ); 44 45 crc32_check crc32_check 46 ( 47 .reset (I_reset), 48 .clk (I_gmii_rclk), 49 .CRC32_en (crc_en & I_crc32_en), 50 .CRC32_init (~mac_rvalid_r4), 51 .data (mac_rdata_r4), 52 .CRC_data (crc_data_out) 53 ); 54 55 //mac帧控制,当接收方来不及处理接收数据,需要进行帧控制,通知发送模块。 56 uimac_tx_frame_ctrl mac_tx_frame_ctrl 57 ( 58 .I_clk (I_gmii_rclk), 59 .I_reset (I_reset), 60 .I_mac_pause_en (mac_rvalid_r4 & mac_pause_en), 61 .I_mac_data (mac_rdata_r4), 62 .O_mac_pause_en (O_mac_pause_en), 63 .O_mac_pause_time (O_mac_pause_time) 64 );
通过状态机分别对FIFO写入数据和FIFO读出数据的时序进行控制。FIFO写入数据的状态机转换图如图所示。
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 91 mac_wfifo_en <= 1'b0; 92 mac_wfifo_en_info <= 1'b1;//写帧信息fifo使能 93 crc_en <= 1'b0; 94 crc_cnt <= 3'd4; 95 STATE <= RECORD_FRAME_LENGTH;//写入帧信息到帧信息fifo 96 end 97 else 98 crc_cnt <= crc_cnt - 1'b1;//crc计算计数器 99 end 100 end 101 RECORD_FRAME_LENGTH:begin//写帧信息完成后,回到状态机WAIT_SFD 102 mac_wfifo_en_info <= 1'b0; 103 mac_wfifo_data_cnt_info <= 11'd0; 104 STATE <= WAIT_SFD;//回到帧探测状态机 105 end 106 WAIT_FRAME_END:begin//等待帧结束 107 if(mac_rvalid_r4) 108 STATE <= WAIT_FRAME_END; 109 else begin 110 crc_en <= 1'b0; 111 mac_pause_en <= 1'b0; 112 STATE <= WAIT_SFD; 113 end 114 end 115 endcase 116 end 117 end
FIFO读出数据的状态机转换图如图所示。
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文件中找到。
控制FIFO写入数据的状态机跳转图如所示。
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读出数据的状态机转换图如图所示。
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 37 mac_rfifo_data_length <= mac_rfifo_data_info[26:16];//数据长度 38 mac_rfifo_en_info <= 1'b0; 39 40 if(pause_flag && mac_rfifo_data_info[74:27] == pause_dst_mac_addr) begin//如果存在PAUSE帧需要发送,并且目的地址和当前目的地址一致 41 mac_rfifo_data_en <= 1'b0;//PAUSE 帧阶段不从FIFO读数据 42 S_RFIFO <= WAIT_PAUSE_END;//等待PAUSE流控制结束 43 end 44 else begin 45 mac_rfifo_data_en <= 1'b1; 46 S_RFIFO <= READ_DATA_PACKET; 47 end 48 end 49 READ_DATA_PACKET:begin 50 if(mac_rfifo_data_cnt == (mac_rfifo_data_length - 1'b1)) begin//一帧数据从FIFO读完(上一个状态已经读出了一个周期,所以这里少计一个数) 51 mac_rfifo_data_en <= 1'b0; 52 mac_rfifo_data_length <= 11'd0; 53 mac_rfifo_data_cnt <= 11'd0; 54 mac_rfifo_data_addr <= 48'd0; 55 ether_type <= 16'd0; 56 S_RFIFO <= WAIT_CRC_TRANS_DONE; 57 end 58 else begin 59 mac_rfifo_data_en <= 1'b1; 60 mac_rfifo_data_cnt <= mac_rfifo_data_cnt + 1'b1; 61 S_RFIFO <= READ_DATA_PACKET; 62 end 63 end 64 WAIT_CRC_TRANS_DONE:begin//等待正在发送的MAC数据包CRC发送完成 65 if(crc_cnt) 66 S_RFIFO <= WAIT_CRC_TRANS_DONE; 67 else 68 S_RFIFO <= ADD_IFG; 69 end 70 ADD_IFG:begin//数据包发送后,插入帧间隔,2帧之间最少需要IFGmini=96bit/speed,比如1000M 96ns 100M 960ns 10M 9600ns 71 if(inter_gap_cnt == (IFG - 4'd4)) begin//插入最小帧间隔周期,在此状态机,MAC_SEND_FLOW_CONTROL 流控制模可以发送PAUSE帧,减去4'd4是本计数器结束后,距离下一帧发送实际需要还要经过4个时钟周期 72 inter_gap_cnt <= 4'd0; 73 S_RFIFO <= WAIT_DATA_PACKET;//进入WAIT_DATA_PACKET 74 end 75 else begin 76 inter_gap_cnt <= inter_gap_cnt + 1'b1; 77 S_RFIFO <= ADD_IFG; 78 end 79 end 80 WAIT_PAUSE_END:begin//等待暂停结束后重新传输数据 81 if(pause_flag) begin//pause 控制 82 mac_rfifo_data_en <= 1'b0; 83 S_RFIFO <= WAIT_PAUSE_END; 84 end 85 else begin 86 mac_rfifo_data_en <= 1'b1;//暂停结束后,继续读帧FIFO中数据 87 S_RFIFO <= READ_DATA_PACKET; 88 end 89 end 90 endcase 91 end 92 end 93 94 always@(posedge I_gmii_tclk or posedge I_reset) begin 95 if(I_reset) begin 96 O_gmii_tvalid <= 1'b0; 97 mac_tdata <= 8'd0; 98 mac_tdata_crc_en <= 1'b0; 99 data22_cnt <= 5'd0; 100 data22_shift_cnt <= 5'd22; 101 crc_cnt <= 3'd4; 102 crc_read <= 1'b0; 103 end 104 else if(mac_rfifo_data_en) begin 105 case(data22_cnt)//这个阶段移位寄存器进行数据的填充 106 0 :begin mac_tdata <= 8'h55; data22_cnt <= data22_cnt + 1'b1; O_gmii_tvalid <= 1'b1; data22_shift_cnt <= 5'd22;end 107 1 :begin mac_tdata <= 8'h55; data22_cnt <= data22_cnt + 1'b1; end 108 2 :begin mac_tdata <= 8'h55; data22_cnt <= data22_cnt + 1'b1; end 109 3 :begin mac_tdata <= 8'h55; data22_cnt <= data22_cnt + 1'b1; end 110 4 :begin mac_tdata <= 8'h55; data22_cnt <= data22_cnt + 1'b1; end 111 5 :begin mac_tdata <= 8'h55; data22_cnt <= data22_cnt + 1'b1; end 112 6 :begin mac_tdata <= 8'h55; data22_cnt <= data22_cnt + 1'b1; end 113 7 :begin mac_tdata <= 8'hd5; data22_cnt <= data22_cnt + 1'b1; end 114 115 8 :begin mac_tdata <= mac_rfifo_data_addr[47:40]; data22_cnt <= data22_cnt + 1'b1; 116 mac_tdata_crc_en <= 1'b1; end 117 9 :begin mac_tdata <= mac_rfifo_data_addr[39:32]; data22_cnt <= data22_cnt + 1'b1; end 118 10 :begin mac_tdata <= mac_rfifo_data_addr[31:24]; data22_cnt <= data22_cnt + 1'b1; end 119 11 :begin mac_tdata <= mac_rfifo_data_addr[23:16]; data22_cnt <= data22_cnt + 1'b1; end 120 12 :begin mac_tdata <= mac_rfifo_data_addr[15:8]; data22_cnt <= data22_cnt + 1'b1; end 121 13 :begin mac_tdata <= mac_rfifo_data_addr[7:0]; data22_cnt <= data22_cnt + 1'b1; end 122 14 :begin mac_tdata <= I_mac_local_addr[47:40]; data22_cnt <= data22_cnt + 1'b1; end 123 15 :begin mac_tdata <= I_mac_local_addr[39:32]; data22_cnt <= data22_cnt + 1'b1; end 124 16 :begin mac_tdata <= I_mac_local_addr[31:24]; data22_cnt <= data22_cnt + 1'b1; end 125 17 :begin mac_tdata <= I_mac_local_addr[23:16]; data22_cnt <= data22_cnt + 1'b1; end 126 18 :begin mac_tdata <= I_mac_local_addr[15:8]; data22_cnt <= data22_cnt + 1'b1; end 127 19 :begin mac_tdata <= I_mac_local_addr[7:0]; data22_cnt <= data22_cnt + 1'b1; end 128 129 20 :begin mac_tdata <= ether_type[15:8]; data22_cnt <= data22_cnt + 1'b1; end 130 21 :begin mac_tdata <= ether_type[7:0]; data22_cnt <= data22_cnt + 1'b1; end 131 22 :begin mac_tdata <= mac_tdata_shift_out; end 132 default: data22_cnt <= 5'd0; 133 endcase 134 end 135 else if(!mac_rfifo_data_en) begin//tmac_en=1阶段会读取mac_tx_frame_info_fifo中所有的数据写到移位寄存器,当tmac_en=0,移位寄存器剩余22个有效数据需要移除 136 if(data22_shift_cnt != 5'd0) begin//将移位寄存器组中的剩余22个数据读出 137 mac_tdata <= mac_tdata_shift_out; 138 data22_shift_cnt <= data22_shift_cnt - 1'b1; 139 end 140 else begin 141 if(I_crc32_en && O_gmii_tvalid) begin //开始传送帧的CRC32校验值 142 O_gmii_tvalid <= 1'b1; 143 data22_cnt <= 5'd0; 144 mac_tdata_crc_en <= 1'b0;//停止CRC计算 145 crc_read <= 1'b1;//开始传输CRC32校验值 146 if(crc_cnt != 3'd0) 147 crc_cnt <= crc_cnt - 1'b1; 148 else begin 149 O_gmii_tvalid <= 1'b0; 150 crc_read <= 1'b0;//4字节的CRC校验值传输完毕 151 crc_cnt <= 3'd4; 152 end 153 end 154 else begin//不进行CRC32校验,无需传输校验值 155 O_gmii_tvalid <= 1'b0; 156 data22_shift_cnt <= 5'd0; 157 end 158 end 159 end 160 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 16 reg [15:0] opcode; 17 reg [15:0] pause_time;//pause_time字段为发送MAC停止发送数据的时间,每单位为512bit传输时间,比如数值为16’d1024表示暂停时间为MAC传输1024*512bit数据所需要的时间 18 reg [2:0] cnt; 19 reg STATE; 20 21 localparam READ_FRAME = 0; 22 localparam WAIT_FRAME_END = 1; 23 24 localparam PAUSE_FRAME = 16'h0001;//操作码,固定值为0x0001 25 26 always@(posedge I_clk or posedge I_reset) begin 27 if(I_reset) begin 28 cnt <= 3'd0; 29 opcode <= 16'd0; 30 pause_time <= 16'd0; 31 O_mac_pause_en <= 1'b0; 32 O_mac_pause_time <= 22'd0; 33 STATE <= READ_FRAME; 34 end 35 else begin 36 case(STATE) 37 READ_FRAME:begin 38 if(I_mac_pause_en)//帧流控制有效 39 case(cnt) 40 0:begin opcode[15: 8] <= I_mac_data; cnt <= cnt + 1'b1;end 41 1:begin 42 opcode[ 7: 0] <= I_mac_data; 43 if({opcode[15: 8], I_mac_data} == PAUSE_FRAME) begin//判断是PAUSE帧 44 STATE <= READ_FRAME; 45 cnt <= cnt + 1'b1; 46 end 47 else begin 48 STATE <= WAIT_FRAME_END; 49 cnt <= 3'd0; 50 end 51 end 52 2:begin pause_time[15: 8] <= I_mac_data; cnt <= cnt + 1'b1;end 53 3:begin pause_time[ 7: 0] <= I_mac_data; cnt <= cnt + 1'b1;end//需要暂停发送的时间 54 4:begin 55 cnt <= 3'd0; 56 opcode <= 16'd0; 57 pause_time <= 16'd0; 58 O_mac_pause_en <= 1'b1;//通知MAC发送控制器,接收到了PAUSE帧 59 O_mac_pause_time <= {pause_time, 6'd0};//*512/8 = *64 = *(2^6) 60 STATE <= WAIT_FRAME_END;//等待帧结束 61 end 62 endcase 63 else 64 STATE <= READ_FRAME; 65 end 66 WAIT_FRAME_END:begin//等待帧结束 67 O_mac_pause_time <= 22'd0; 68 O_mac_pause_en <= 1'b0; 69 if(I_mac_pause_en) 70 STATE <= WAIT_FRAME_END; 71 else 72 STATE <= READ_FRAME; 73 end 74 endcase 75 end 76 end 77 78 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 18 reg [21:0] pause_clk_num; 19 reg [21:0] pause_clk_cnt; 20 reg [1:0] STATE; 21 22 localparam WAIT_PAUSE_FRAME = 2'd0; 23 localparam WAIT_CURRENT_SEND_DONE = 2'd1; 24 localparam MAC_SEND_PAUSE = 2'd2; 25 26 localparam ADD_IFG = 3'd4; 27 28 always@(posedge I_clk or posedge I_reset) begin 29 if(I_reset) begin 30 pause_clk_num <= 22'd0; 31 pause_clk_cnt <= 22'd0; 32 O_pause_flag <= 1'b0; 33 O_pause_dst_mac_addr <= 48'd0; 34 STATE <= WAIT_PAUSE_FRAME; 35 end 36 else begin 37 case(STATE) 38 WAIT_PAUSE_FRAME:begin//等待PAUSE帧 39 O_pause_flag <= 1'b0; 40 if(I_mac_pause_en) begin //MAC接收模块接收到PAUSE帧 41 O_pause_dst_mac_addr <= I_mac_pause_addr;//MAC发送模块需要发送PAUSE的目的MAC地址 42 pause_clk_num <= I_mac_pause_time;//PAUSE时间,在MAC接收端已经换算好需要PAUSE的时钟周期个数 43 STATE <= WAIT_CURRENT_SEND_DONE; 44 end 45 else begin 46 O_pause_dst_mac_addr <= 48'd0; 47 pause_clk_num <= 22'd0; 48 STATE <= WAIT_PAUSE_FRAME; 49 end 50 end 51 WAIT_CURRENT_SEND_DONE:begin//等待当MAC发送状态机在I_mac_state == ADD_IFG状态的时候,设置O_pause_flag标志 52 if(I_mac_state == ADD_IFG) begin 53 O_pause_flag <= 1'b1;//设置O_pause_flag,通知MAC 帧发送模块,暂停数据发送 54 STATE <= MAC_SEND_PAUSE; 55 end 56 else begin 57 O_pause_flag <= 1'b0; 58 STATE <= WAIT_CURRENT_SEND_DONE; 59 end 60 end 61 MAC_SEND_PAUSE:begin//暂停数据发送,等待(pause_clk_num - 3)个时钟周期 62 if(pause_clk_cnt == (pause_clk_num - 3)) begin 63 O_pause_flag <= 1'b0; 64 O_pause_dst_mac_addr <= 48'd0; 65 pause_clk_cnt <= 22'd0; 66 pause_clk_num <= 22'd0; 67 STATE <= WAIT_PAUSE_FRAME; 68 end 69 else begin 70 O_pause_flag <= 1'b1;//设置O_pause_flag,通知MAC 帧发送模块,暂停数据发送 71 pause_clk_cnt <= pause_clk_cnt + 1'b1; 72 STATE <= MAC_SEND_PAUSE; 73 end 74 end 75 endcase 76 end 77 end 78 79 endmodule
本文来米联客(milianke),作者:米联客(milianke),转载请注明原文链接:https://www.cnblogs.com/milianke/p/18351339