软件版本:Anlogic -TD5.9.1-DR1_ES1.1 操作系统:WIN10 64bit 硬件平台:适用安路(Anlogic)FPGA 实验平台:米联客-MLK-L1-CZ06-DR1M90G开发板 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接收时钟和用户接口时钟之间的时钟域的转换,并将数据包输出至上层。 - /*******************************uimac_rx模块*********************
- --以下是米联客设计的uimac_rx模块
- --本模块主要有以下几个功能
- --1. 从外部PHY芯片接收mac帧,解析mac帧首部,进行mac地址过滤和帧类型过滤。
- --2. 内部子模块crc32_check对每帧数据进行crc32值的计算,判断数据的正确性。
- --3. 识别接收的mac流控帧,子模块mac_frame_ctrl提取流控帧中的暂停时间和源mac地址输出至uimac_tx模块。
- --4. mac_rx_data_fifo完成phy接收时钟和用户接口时钟之间的时钟域转换,将数据输出。
- *********************************************************************/
- `timescale 1ns/1ps
- module uimac_rx
- (
- input wire [47:0] I_mac_local_addr , //本地MAC地址
- input wire I_crc32_en , //使能CRC校验
- input wire I_reset , //系统复位
- //MAC接收数据发送给上层协议
- input wire I_mac_rclk , //接收时钟
- output wire O_mac_rvalid , //MAC帧数据有效
- output wire [7:0] O_mac_rdata , //MAC有效数据
- output wire [15:0] O_mac_rdata_type , //MAC类型
- output wire O_mac_rdata_error ,
- //发送PAUSE控制到mac_send
- output wire O_mac_pause_en ,
- output wire [21:0] O_mac_pause_time ,
- output wire [47:0] O_mac_pause_addr ,
- //从硬件层获取的裸MAC数据
- input wire I_gmii_rclk , //rgmii接收时钟
- input wire I_gmii_rvalid , //gmii接收数据有效使能信号
- input wire [7:0] I_gmii_rdata //gmii接收数据
- );
- wire [7:0] mac_rdata;
- reg mac_rdata_valid;
- reg [15:0] mac_rdata_type;
- reg mac_rdata_error;
- assign O_mac_rdata = mac_rdata;
- assign O_mac_rvalid = mac_rdata_valid;
- assign O_mac_rdata_type = mac_rdata_type;
- assign O_mac_rdata_error = mac_rdata_error;
- reg [10:0] mac_rdata_cnt;
- reg mac_wfifo_en; //FIFO写入数据有效使能
- reg mac_rfifo_en; //FIFO读数据使能
- reg crc_en; //crc校验使能
- reg [2:0] crc_cnt; //移位计数器
- wire [31:0] crc_data_out; //crc校验结果输出
- reg [2:0] STATE; //写FIFO状态机
- reg [1:0] S_RFIFO; //读FIFO状态机
- reg [47:0] dst_mac_addr; //MAC帧解析出的目的MAC地址(接收方的MAC地址)
- reg [47:0] src_mac_addr; //MAC帧解析出的源MAC地址(发送方的MAC地址)
- reg [15:0] mac_frame_type; //上层数据包类型(0x0800 ip;0x0806 arp;0x8808 mac_ctrl)
- reg mac_pause_en; //PAUSE帧有效使能
- reg [3:0] cnt; //对MAC帧头部的字节数计数
- reg [10:0] mac_wfifo_data_cnt_info;//写帧信息字节数到信息FIFO
- reg mac_wfifo_en_info; //写帧信息FIFO有效使能
- reg mac_rfifo_en_info; //读帧信息FIFO有效使能
- wire [26:0] mac_rfifo_data_info; //从信息FIFO读出帧信息
- reg [10:0] mac_rdata_len; //mac帧长度
- wire mac_rfifo_empty_info; //信息FIFO读空信号
- reg [7:0] mac_rdata_r1, mac_rdata_r2, mac_rdata_r3, mac_rdata_r4;//打拍
- reg mac_rvalid_r1, mac_rvalid_r2, mac_rvalid_r3, mac_rvalid_r4;//打拍
- localparam WAIT_SFD = 3'd0;
- localparam CHECK_MAC_HEADER = 3'd1;
- localparam WRITE_FIFO = 3'd2;
- localparam RECORD_FRAME_LENGTH = 3'd3;
- localparam WAIT_FRAME_END = 3'd4;//STATE
- localparam WAIT_MAC_FRAME = 2'd0;
- localparam READ_MAC_FRAME_DATA_LENGTH = 2'd1;
- localparam READ_MAC_FRAME_DATA = 2'd2;//S_RFIFO
- localparam ARP_TYPE = 16'h0806;
- localparam IP_TYPE = 16'h0800;
- localparam MAC_CONTROL_TYPE = 16'h8808;
复制代码由于用户接口时钟和PHY芯片接收时钟不同源,因此需要对输入的数据进行跨时钟域处理,再将数据传至上层。由于需要传输的数据量比较大,且用FIFO做跨时钟域比较简单,所以程序中使用异步FIFO做跨时钟域处理。 在uimac_rx和uimac_tx发送模块中,都使用了两个FIFO做跨时钟域处理,一个FIFO用来缓存数据,另一个FIFO用来缓存需要传递的信息,在一帧输入接收完成后,将信息写入帧信息FIFO,通过FIFO空标志信号来控制一帧数据读出。 信号打4拍再写入数据FIFO是为了过滤掉4字节CRC校验位。 - assign O_mac_pause_addr = src_mac_addr;
- always@(posedge I_gmii_rclk) begin
- mac_rdata_r1 <= I_gmii_rdata;
- mac_rdata_r2 <= mac_rdata_r1;
- mac_rdata_r3 <= mac_rdata_r2;
- mac_rdata_r4 <= mac_rdata_r3;
- end
- always@(posedge I_gmii_rclk) begin
- mac_rvalid_r1 <= I_gmii_rvalid;
- mac_rvalid_r2 <= mac_rvalid_r1;
- mac_rvalid_r3 <= mac_rvalid_r2;
- mac_rvalid_r4 <= mac_rvalid_r3;
- end//打4拍,方便fifo只写入有效数据,而不写入crc校验位
- mac_rx_data_fifo mac_rx_data_fifo (
- .rst (I_reset),
- .wr_clk (I_gmii_rclk),
- .din (mac_rdata_r4),
- .wr_en (mac_wfifo_en & I_gmii_rvalid),//mac_wfifo_en控制只写入有效数据部分,I_gmii_rvalid控制最后的CRC部分不写入
- .rd_clk (I_mac_rclk),
- .rd_en (mac_rfifo_en),
- .dout (mac_rdata),
- .full (),
- .empty (),
- .rd_data_count (),
- .wr_data_count ()
- );
- mac_rx_frame_fifo mac_rx_frame_fifo (
- .rst (I_reset),
- .wr_clk (I_gmii_rclk),
- .din ({mac_wfifo_data_cnt_info,mac_frame_type}),
- .wr_en (mac_wfifo_en_info),
- .rd_clk (I_mac_rclk),
- .rd_en (mac_rfifo_en_info),
- .dout (mac_rfifo_data_info),
- .full (),
- .empty (mac_rfifo_empty_info)
- );
- crc32_check crc32_check
- (
- .reset (I_reset),
- .clk (I_gmii_rclk),
- .CRC32_en (crc_en & I_crc32_en),
- .CRC32_init (~mac_rvalid_r4),
- .data (mac_rdata_r4),
- .CRC_data (crc_data_out)
- );
- //mac帧控制,当接收方来不及处理接收数据,需要进行帧控制,通知发送模块。
- uimac_tx_frame_ctrl mac_tx_frame_ctrl
- (
- .I_clk (I_gmii_rclk),
- .I_reset (I_reset),
- .I_mac_pause_en (mac_rvalid_r4 & mac_pause_en),
- .I_mac_data (mac_rdata_r4),
- .O_mac_pause_en (O_mac_pause_en),
- .O_mac_pause_time (O_mac_pause_time)
- );
复制代码通过状态机分别对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状态,等待接收下一帧。 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状态。 - always@(posedge I_mac_rclk or posedge I_reset) begin
- if(I_reset) begin
- mac_rfifo_en_info <= 1'b0;
- mac_rdata_len <= 11'd0;
- mac_rdata_cnt <= 11'd0;
- mac_rfifo_en <= 1'b0;
- mac_rdata_type <= 16'd0;
- mac_rdata_valid <= 1'b0;
- S_RFIFO <= WAIT_MAC_FRAME;
- end
- else begin
- case(S_RFIFO)
- WAIT_MAC_FRAME:begin
- if(!mac_rfifo_empty_info) begin//接收mac帧信息fifo非空
- mac_rfifo_en_info <= 1'b1;
- S_RFIFO <= READ_MAC_FRAME_DATA_LENGTH;
- end
- else
- S_RFIFO <= WAIT_MAC_FRAME;
- end
- READ_MAC_FRAME_DATA_LENGTH:begin
- mac_rdata_len <= mac_rfifo_data_info[26:16];//mac帧长度
- mac_rdata_type <= mac_rfifo_data_info[15:0];//mac类型
- mac_rfifo_en_info <= 1'b0;
- mac_rfifo_en <= 1'b1;//读数据fifo
- mac_rdata_valid <= 1'b1;//数据有效
- S_RFIFO <= READ_MAC_FRAME_DATA;
- end
- READ_MAC_FRAME_DATA:begin
- if(mac_rdata_cnt < (mac_rdata_len - 1'b1)) begin//读完一帧数据
- mac_rdata_cnt <= mac_rdata_cnt + 1'b1;
- S_RFIFO <= READ_MAC_FRAME_DATA;
- end
- else begin
- mac_rfifo_en <= 1'b0;
- mac_rdata_valid <= 1'b0;
- mac_rdata_cnt <= 11'd0;
- mac_rdata_len <= 11'd0;
- mac_rdata_type <= 16'd0;
- S_RFIFO <= WAIT_MAC_FRAME;
- end
- end
- endcase
- end
- 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状态,等待接收下一帧数据。 - always@(posedge I_mac_tclk or posedge rst) begin
- if(rst) begin
- mac_wfifo_en_info <= 1'b0; //MAC消息FIFO,把MAC的信息包括,目的MAC地址、有效数据长度、帧类型写入到info fifo暂存
- mac_wfifo_data_addr_info <= 48'd0; //MAC目的地址,暂存info fifo
- mac_wfifo_data_type_info <= 16'd0; //MAC帧类型,暂存info fifo
- mac_wfifo_data_cnt_info <= 11'd0; //MAC数据部分发送字节计数器
- mac_wfifo_en <= 1'b0; //将帧数据写入到mac_tx_data_fifo缓存
- mac_wfifo_data <= 8'd0; //将帧数据写入到mac_tx_data_fifo缓存
- O_mac_tbusy <= 1'b1; //通知外部模块,非忙
- S_WFIFO <= WAIT_DATA_PACKET;
- end
- else begin
- case(S_WFIFO)
- WAIT_DATA_PACKET:begin
- if(mac_wfifo_data_cnt > SEND_PAUSE_THRESHOLD) begin//当FIFO写通道数据计数器大于SEND_PAUSE_THRESHOLD,不进行新的一帧传输,O_mac_tbusy为握手信号,不进行握手(拉高)
- O_mac_tbusy <= 1'b0;
- S_WFIFO <= WAIT_DATA_PACKET;
- end
- else begin
- if(I_mac_tvalid) begin//当有效数据发送过来后开始接收数据并且缓存到FIFO
- O_mac_tbusy <= 1'b1; //uimac_tx 忙
- mac_wfifo_en <= 1'b1; //将数据写入FIFO
- mac_wfifo_data <= I_mac_tdata; //写入FIFO的数据
- mac_wfifo_data_addr_info<= I_mac_tdest_addr; //目的MAC地址
- mac_wfifo_data_type_info<= {14'd0, I_mac_tdata_type};//数据类型
- mac_wfifo_data_cnt_info <= mac_wfifo_data_cnt_info + 1'b1;//一帧数据的长度,以BYTE为单位
- S_WFIFO <= WRITE_FIFO;//进入下一个状态等待写FIFO
- end
- else begin
- O_mac_tbusy <= 1'b0; //uimac_tx 非忙
- S_WFIFO <= WAIT_DATA_PACKET;
- end
- end
- end
- WRITE_FIFO:begin//写数据到FIFO该FIFO用于缓存udp协议发送过来的数据
- if(I_mac_tvalid) begin//一帧数据接收过程中O_gmii_tdata_valid始终为高电平
- mac_wfifo_en <= 1'b1;//继续写FIFO
- mac_wfifo_data <= I_mac_tdata;//写入FIFO的数据
- mac_wfifo_data_cnt_info <= mac_wfifo_data_cnt_info + 1'b1;//帧字节计数器累加
- S_WFIFO <= WRITE_FIFO;
- end
- else begin
- if(mac_wfifo_data_cnt_info < 11'd46) begin//当一包/帧数据的长度小于46字节,自动补0(一帧数据最小64bytes,其中数据部分最小46bytes)
- mac_wfifo_en <= 1'b1;
- mac_wfifo_data <= 8'd0;
- mac_wfifo_en_info <= 1'b0;
- mac_wfifo_data_cnt_info <= mac_wfifo_data_cnt_info + 1'b1;
- S_WFIFO <= WRITE_FIFO;
- end
- else begin//当一包/帧数据接收完,写包/帧信息 到包/帧信息FIFO
- mac_wfifo_en <= 1'b0;
- mac_wfifo_data <= 8'd0;
- mac_wfifo_en_info <= 1'b1;
- S_WFIFO <= RECORD_DATA_PACKET_INFO;
- end
- end
- end
- RECORD_DATA_PACKET_INFO:begin//时序中,该周期完成写包/帧信息 到包/帧信息FIFO
- mac_wfifo_en_info <= 1'b0;
- mac_wfifo_data_addr_info <= 48'd0;
- mac_wfifo_data_type_info <= 16'd0;
- mac_wfifo_data_cnt_info <= 11'd0;
- S_WFIFO <= WAIT_DATA_PACKET;
- end
- endcase
- end
- 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校验数据。 3.1.3 CRC校验模块通过网页生成CRC校验代码,将代码稍作修改,得到CRC校验计算模块。注意uimac_rx模块将接收到的CRC校验位也进行CRC校验,若校验计算结果为0则校验正确。 - `timescale 1ns / 1ps
- module crc32_gen(
- input reset,
- input clk,
- input CRC32_en, //CRC校验使能信号
- input CRC32_init, //CRC校验值初始化信号
- input CRC_read,
- //input CRC32_valid, //CRC校验值维持有效
- input [7:0] data,
- output [7:0] CRC_out
- );
- reg [31:0] CRC_temp;
- assign CRC_out = CRC_read ? ~{CRC_temp[24], CRC_temp[25], CRC_temp[26], CRC_temp[27],
- CRC_temp[28], CRC_temp[29], CRC_temp[30], CRC_temp[31]} : 8'h00;
-
- always@(posedge clk or posedge reset)
- if(reset)
- CRC_temp <= 32'hffffffff;
- else if(CRC32_init)
- CRC_temp <= 32'hffffffff;
- else if(CRC32_en)
- begin
- CRC_temp[0]<=CRC_temp[24]^CRC_temp[30]^data[1]^data[7];
- CRC_temp[1]<=CRC_temp[25]^CRC_temp[31]^data[0]^data[6]^CRC_temp[24]^CRC_temp[30]^data[1]^data[7];
- 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]
- ^data[7];
- CRC_temp[3]<=CRC_temp[27]^data[4]^CRC_temp[26]^data[5]^CRC_temp[25]^CRC_temp[31]^data[0]^data[6];
- 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]
- ^data[7];
- 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];
- 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];
- 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];
- 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];
- 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];
- 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];
- 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];
- 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];
- 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];
- 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];
- 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];
- CRC_temp[16]<=CRC_temp[8]^CRC_temp[29]^data[2]^CRC_temp[28]^data[3]^CRC_temp[24]^data[7];
- CRC_temp[17]<=CRC_temp[9]^CRC_temp[30]^data[1]^CRC_temp[29]^data[2]^CRC_temp[25]^data[6];
- CRC_temp[18]<=CRC_temp[10]^CRC_temp[31]^data[0]^CRC_temp[30]^data[1]^CRC_temp[26]^data[5];
- CRC_temp[19]<=CRC_temp[11]^CRC_temp[31]^data[0]^CRC_temp[27]^data[4];
- CRC_temp[20]<=CRC_temp[12]^CRC_temp[28]^data[3];
- CRC_temp[21]<=CRC_temp[13]^CRC_temp[29]^data[2];
- CRC_temp[22]<=CRC_temp[14]^CRC_temp[24]^data[7];
- CRC_temp[23]<=CRC_temp[15]^CRC_temp[25]^data[6]^CRC_temp[24]^CRC_temp[30]^data[1]^data[7];
- CRC_temp[24]<=CRC_temp[16]^CRC_temp[26]^data[5]^CRC_temp[25]^CRC_temp[31]^data[0]^data[6];
- CRC_temp[25]<=CRC_temp[17]^CRC_temp[27]^data[4]^CRC_temp[26]^data[5];
- 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];
- 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];
- CRC_temp[28]<=CRC_temp[20]^CRC_temp[30]^data[1]^CRC_temp[29]^data[2]^CRC_temp[26]^data[5];
- CRC_temp[29]<=CRC_temp[21]^CRC_temp[31]^data[0]^CRC_temp[30]^data[1]^CRC_temp[27]^data[4];
- CRC_temp[30]<=CRC_temp[22]^CRC_temp[31]^data[0]^CRC_temp[28]^data[3];
- CRC_temp[31]<=CRC_temp[23]^CRC_temp[29]^data[2];
- end
- else if(CRC_read)
- CRC_temp <= {CRC_temp[23:0], 8'hff};
-
- endmodule
复制代码 3.1.4 PAUSE帧流控制模块uimac_rx模块接收到PAUSE帧后,会将有效数据段送入mac_tx_frame_ctrl模块进行解析,得到暂停时间和PAUSE帧发送方的MAC地址,将其发送给uimac_tx模块的mac_tx_pause_ctrl子模块。 - /*******************************mac_tx_frame_ctrl模块*********************
- --以下是米联客设计的mac_tx_frame_ctrl模块,用于产生MAC发送模块的PAUSE暂停发送
- 1.
- *********************************************************************/
- `timescale 1ns/1ps
- module uimac_tx_frame_ctrl
- (
- input wire I_clk,
- input wire I_reset,
- input wire I_mac_pause_en,
- input wire [7:0] I_mac_data,
- output reg O_mac_pause_en,
- output reg [21:0] O_mac_pause_time//发送MAC停止发送数据的时间
- );
- reg [15:0] opcode;
- reg [15:0] pause_time;//pause_time字段为发送MAC停止发送数据的时间,每单位为512bit传输时间,比如数值为16’d1024表示暂停时间为MAC传输1024*512bit数据所需要的时间
- reg [2:0] cnt;
- reg STATE;
- localparam READ_FRAME = 0;
- localparam WAIT_FRAME_END = 1;
- localparam PAUSE_FRAME = 16'h0001;//操作码,固定值为0x0001
- always@(posedge I_clk or posedge I_reset) begin
- if(I_reset) begin
- cnt <= 3'd0;
- opcode <= 16'd0;
- pause_time <= 16'd0;
- O_mac_pause_en <= 1'b0;
- O_mac_pause_time <= 22'd0;
- STATE <= READ_FRAME;
- end
- else begin
- case(STATE)
- READ_FRAME:begin
- if(I_mac_pause_en)//帧流控制有效
- case(cnt)
- 0:begin opcode[15: 8] <= I_mac_data; cnt <= cnt + 1'b1;end
- 1:begin
- opcode[ 7: 0] <= I_mac_data;
- if({opcode[15: 8], I_mac_data} == PAUSE_FRAME) begin//判断是PAUSE帧
- STATE <= READ_FRAME;
- cnt <= cnt + 1'b1;
- end
- else begin
- STATE <= WAIT_FRAME_END;
- cnt <= 3'd0;
- end
- end
- 2:begin pause_time[15: 8] <= I_mac_data; cnt <= cnt + 1'b1;end
- 3:begin pause_time[ 7: 0] <= I_mac_data; cnt <= cnt + 1'b1;end//需要暂停发送的时间
- 4:begin
- cnt <= 3'd0;
- opcode <= 16'd0;
- pause_time <= 16'd0;
- O_mac_pause_en <= 1'b1;//通知MAC发送控制器,接收到了PAUSE帧
- O_mac_pause_time <= {pause_time, 6'd0};//*512/8 = *64 = *(2^6)
- STATE <= WAIT_FRAME_END;//等待帧结束
- end
- endcase
- else
- STATE <= READ_FRAME;
- end
- WAIT_FRAME_END:begin//等待帧结束
- O_mac_pause_time <= 22'd0;
- O_mac_pause_en <= 1'b0;
- if(I_mac_pause_en)
- STATE <= WAIT_FRAME_END;
- else
- STATE <= READ_FRAME;
- end
- endcase
- end
- end
- endmodule
复制代码mac_tx_pause_ctrl接收到mac_pasue_en信号为高时,将接收的信息寄存,状态机跳转,等待MAC发送端发送完一帧数据,进入帧间隔等待。当MAC发送模块进入帧间隔状态后,流控模块拉高pause_flag信号,等待暂停时间结束后将信号拉低。 - /*******************************mac_tx_pause_ctrl模块*********************
- --以下是米联客设计的mac_tx_pause_ctrl MAC发送端,流控制器模块
- 1.
- *********************************************************************/
- `timescale 1ns/1ps
- module uimac_tx_pause_ctrl
- (
- input wire I_clk,
- input wire I_reset,
- input wire [2:0] I_mac_state,
- input wire I_mac_pause_en,
- input wire [21:0] I_mac_pause_time,
- input wire [47:0] I_mac_pause_addr,
- output reg [47:0] O_pause_dst_mac_addr,
- output reg O_pause_flag
- );
- reg [21:0] pause_clk_num;
- reg [21:0] pause_clk_cnt;
- reg [1:0] STATE;
- localparam WAIT_PAUSE_FRAME = 2'd0;
- localparam WAIT_CURRENT_SEND_DONE = 2'd1;
- localparam MAC_SEND_PAUSE = 2'd2;
- localparam ADD_IFG = 3'd4;
- always@(posedge I_clk or posedge I_reset) begin
- if(I_reset) begin
- pause_clk_num <= 22'd0;
- pause_clk_cnt <= 22'd0;
- O_pause_flag <= 1'b0;
- O_pause_dst_mac_addr <= 48'd0;
- STATE <= WAIT_PAUSE_FRAME;
- end
- else begin
- case(STATE)
- WAIT_PAUSE_FRAME:begin//等待PAUSE帧
- O_pause_flag <= 1'b0;
- if(I_mac_pause_en) begin //MAC接收模块接收到PAUSE帧
- O_pause_dst_mac_addr <= I_mac_pause_addr;//MAC发送模块需要发送PAUSE的目的MAC地址
- pause_clk_num <= I_mac_pause_time;//PAUSE时间,在MAC接收端已经换算好需要PAUSE的时钟周期个数
- STATE <= WAIT_CURRENT_SEND_DONE;
- end
- else begin
- O_pause_dst_mac_addr <= 48'd0;
- pause_clk_num <= 22'd0;
- STATE <= WAIT_PAUSE_FRAME;
- end
- end
- WAIT_CURRENT_SEND_DONE:begin//等待当MAC发送状态机在I_mac_state == ADD_IFG状态的时候,设置O_pause_flag标志
- if(I_mac_state == ADD_IFG) begin
- O_pause_flag <= 1'b1;//设置O_pause_flag,通知MAC 帧发送模块,暂停数据发送
- STATE <= MAC_SEND_PAUSE;
- end
- else begin
- O_pause_flag <= 1'b0;
- STATE <= WAIT_CURRENT_SEND_DONE;
- end
- end
- MAC_SEND_PAUSE:begin//暂停数据发送,等待(pause_clk_num - 3)个时钟周期
- if(pause_clk_cnt == (pause_clk_num - 3)) begin
- O_pause_flag <= 1'b0;
- O_pause_dst_mac_addr <= 48'd0;
- pause_clk_cnt <= 22'd0;
- pause_clk_num <= 22'd0;
- STATE <= WAIT_PAUSE_FRAME;
- end
- else begin
- O_pause_flag <= 1'b1;//设置O_pause_flag,通知MAC 帧发送模块,暂停数据发送
- pause_clk_cnt <= pause_clk_cnt + 1'b1;
- STATE <= MAC_SEND_PAUSE;
- end
- end
- endcase
- end
- end
- endmodule
复制代码 |