一、前言
之前的一篇文章讲解了64B/66B的基本原理,本篇在基于64B/66B GT Transceiver的基础之上设计自定义PHY。基本框图如下。
二、GT Mdule
GT Module就按照4个GT CHannel共享一个GT COMMON进行设置,如下图。要将例子工程中的GT COMMON取出,使其控制多个通道。如果按照例子工程的架构,当例化多个通道时,工程会出错,原因就是一个GT QARD只有一个GT COMMON。
GT Channel包括一个时钟模块,通过IP核给出来的TXOUTCLK生成TXUSERCLK2、RXUSERCLK2等时钟。GT IP就是例化的一个64B/66B编码的7系列GT Transceiver/
例子工程结构如下:
正确的结构如下:
GT Module主体结构代码如下:
IBUFDS_GTE2 #(
.CLKCM_CFG ("TRUE" ),
.CLKRCV_TRST ("TRUE" ),
.CLKSWING_CFG (2'b11 )
)
IBUFDS_GTE2_inst (
.O (w_gtrefclk ),
.ODIV2 ( ),
.CEB (0 ),
.I (i_gtrefclk_p ),
.IB (i_gtrefclk_n )
);
gtwizard_0_common #
(
.WRAPPER_SIM_GTRESET_SPEEDUP ("TRUE" ),
.SIM_QPLLREFCLK_SEL (QPLLREFCLKSEL_IN )
)
common0_i
(
.QPLLREFCLKSEL_IN (QPLLREFCLKSEL_IN ),
.GTREFCLK0_IN (w_gtrefclk ),
.GTREFCLK1_IN (0 ),
.QPLLLOCK_OUT (w_qplllock ),
.QPLLLOCKDETCLK_IN (i_sysclk ),
.QPLLOUTCLK_OUT (w_qplloutclk ),
.QPLLOUTREFCLK_OUT (w_qplloutrefclk ),
.QPLLREFCLKLOST_OUT (w_qpllrefclklost ),
.QPLLRESET_IN (w_qpllreset|w_common_rst )
);
gtwizard_0_common_reset #
(
.STABLE_CLOCK_PERIOD (50 )
)
common_reset_i
(
.STABLE_CLOCK (i_sysclk ),
.SOFT_RESET (i_tx_rst ),
.COMMON_RESET (w_common_rst )
);
GT_channel GT_channel_u0(
.i_sysclk (i_sysclk ),
.i_gtrefclk (w_gtrefclk ),
.i_rx_rst (i_rx_rst ),
.i_tx_rst (i_tx_rst ),
.o_tx_done (o_tx_done ),
.o_rx_done (o_rx_done ),
.i_tx_polarity (i_tx_polarity ),
.i_tx_diffctrl (i_tx_diffctrl ),
.i_txpostcursor (i_txpostcursor ),
.i_txpercursor (i_txpercursor ),
.i_rx_polarity (i_rx_polarity ),
.i_loopback (i_loopback ),
.i_drpaddr (i_drpaddr ),
.i_drpclk (i_drpclk ),
.i_drpdi (i_drpdi ),
.o_drpdo (o_drpdo ),
.i_drpen (i_drpen ),
.o_drprdy (o_drprdy ),
.i_drpwe (i_drpwe ),
.i_qplllock (w_qplllock ),
.i_qpllrefclklost (w_qpllrefclklost ),
.o_qpllreset (w_qpllreset ),
.i_qplloutclk (w_qplloutclk ),
.i_qplloutrefclk (w_qplloutrefclk ),
.i_data_valid (i_data_valid ),
.o_rx_clk (o_rx_clk ),
.o_rx_data (o_rx_data ),
.o_rx_valid (o_rx_valid ),
.o_rx_header (o_rx_header ),
.o_rx_header_valid (o_rx_header_valid ),
.i_rx_slipbit (i_rx_slipbit ),
.o_tx_clk (o_tx_clk ),
.i_tx_data (i_tx_data ),
.i_tx_header (i_tx_header ),
.i_tx_sequence (i_tx_sequence ),
.o_gt_tx_p (o_gt_tx_p ),
.o_gt_tx_n (o_gt_tx_n ),
.i_gt_rx_p (i_gt_rx_p ),
.i_gt_rx_n (i_gt_rx_n )
);
GT_channel GT_channel_u1(
.i_sysclk (i_sysclk ),
.i_gtrefclk (w_gtrefclk ),
.i_rx_rst (i_rx_rst_2 ),
.i_tx_rst (i_tx_rst_2 ),
.o_tx_done (o_tx_done_2 ),
.o_rx_done (o_rx_done_2 ),
.i_tx_polarity (i_tx_polarity_2 ),
.i_tx_diffctrl (i_tx_diffctrl_2 ),
.i_txpostcursor (i_txpostcursor_2 ),
.i_txpercursor (i_txpercursor_2 ),
.i_rx_polarity (i_rx_polarity_2 ),
.i_loopback (i_loopback_2 ),
.i_drpaddr (i_drpaddr_2 ),
.i_drpclk (i_drpclk_2 ),
.i_drpdi (i_drpdi_2 ),
.o_drpdo (o_drpdo_2 ),
.i_drpen (i_drpen_2 ),
.o_drprdy (o_drprdy_2 ),
.i_drpwe (i_drpwe_2 ),
.i_qplllock (w_qplllock ),
.i_qpllrefclklost (w_qpllrefclklost ),
.o_qpllreset ( ),
.i_qplloutclk (w_qplloutclk ),
.i_qplloutrefclk (w_qplloutrefclk ),
.i_data_valid (i_data_valid_2 ),
.o_rx_clk (o_rx_clk_2 ),
.o_rx_data (o_rx_data_2 ),
.o_rx_valid (o_rx_valid_2 ),
.o_rx_header (o_rx_header_2 ),
.o_rx_header_valid (o_rx_header_valid_2 ),
.i_rx_slipbit (i_rx_slipbit_2 ),
.o_tx_clk (o_tx_clk_2 ),
.i_tx_data (i_tx_data_2 ),
.i_tx_header (i_tx_header_2 ),
.i_tx_sequence (i_tx_sequence_2 ),
.o_gt_tx_p (o_gt_tx_p_2 ),
.o_gt_tx_n (o_gt_tx_n_2 ),
.i_gt_rx_p (i_gt_rx_p_2 ),
.i_gt_rx_n (i_gt_rx_n_2 )
);
三、PHY TX模块
PHY TX模块的主要功能就是封装帧以及大小端转换,以及暂停控制。
- 由于64B/66B编码判定传输开始和传输结束是通过不同的数据帧进行的,所以就要对发送的数据进行封帧处理。
- 在此过程中需要注意将数据由大端模式转换为小端的数据(GT IP核使用的是小端数据)。
- 暂停控制:UG476上指出在使用外部计数器时,当Sequence Counter计数到32时,需要暂停以下,使Gearbox中积累的数据吐出去。
接下来具体说明变速箱GearBox的实现过程,以10GBase-R的物理层为例,若Serdes的位宽要求为32bit,PCS层采用64B/66B的编解码方式,在与Serdes进行数据传输过程中,需要GearBox实现66bit到32bit的转换、32bit到66bit的转换。
继续以上图为例子,XGMII层每拍输出32bit数据,每2拍组合成64bit报文,并且需要编码出2bit的同步头,为了简单化,假设待匹配的Serdes只有1个lane,且位宽为32bit,即GearBox输出是每拍32bit。
显然,输入带宽是大于输出带宽,属于带宽膨胀,因此输入必须暂停,什么时候停呢,见下图波形所示,每32拍停一拍,即待发送的buffer数据积攒够了32bit,此时,产生暂停输入标志位,也就是“反压”住输入信号,让GearBox可以完成数据完整输出。
(引自:详解GearBox设计原理 (qq.com))
基本思想如下:也就是要让Sequence Counter等于32和0的时候输出的数据不变,因为发送数据过程采用的时流水线拼接处理,所以要在Sequence Counter即将等于32的时候去截断数据流(处理过程挺复杂的)。
这个逻辑通过信号w_gt_send_valid 实现,达到30的时候拉低w_gt_send_valid ,这是因为要考虑组帧时候的FIFO读潜伏期。
assign w_gt_send_valid = ro_tx_sequence == 30 ? 0 : 1 ;
另外一个情况需要特殊处理的就是在读第一个FIFO数据的时候 w_gt_send_valid =0的时刻到来,这时候需要特殊处理,因为再组帧的过程中第一帧是需要特殊处理的,如果处理不过会影响到后面的处理过程,时序图可以自己画以下。当检测w_gt_send_valid =0时需要使得读FIFO使能无效一个时钟周期(截流),并使得send_cnt在此时刻的数据保持两个时钟周期,需要使得输出保持。
总之最基本的思想就是:也就是要让Sequence Counter等于32和0的时候输出的数据不变
仿真图如下:
主要代码如下:
always@(posedge i_tx_clk,posedge i_tx_rst)
begin
if(i_tx_rst)
ro_tx_sequence <= 'd0;
else if(ro_tx_sequence == 32)
ro_tx_sequence <= 'd0;
else
ro_tx_sequence <= ro_tx_sequence + 1;
end
always@(posedge i_tx_clk,posedge i_tx_rst)
begin
if(i_tx_rst)
r_input_end <= 'd0;
else if(s_axis_last)
r_input_end <= 'd1;
else if(s_axis_valid)
r_input_end <= 'd0;
else
r_input_end <= r_input_end;
end
always@(posedge i_tx_clk,posedge i_tx_rst)
begin
if(i_tx_rst)
r_send_cnt <= 'd0;
else if(r_input_end && r_send_cnt == r_len - 0)
r_send_cnt <= 'd0;
else if(r_invalid && r_send_cnt)
r_send_cnt <= r_send_cnt;
else if(r_invalid && r_fifo_rden_2d && !r_fifo_rden_3d)
r_send_cnt <= r_send_cnt + 1;
else if((!r_invalid && r_fifo_rden_1d && !r_fifo_rden_2d) || (r_send_cnt&& w_gt_send_valid))
r_send_cnt <= r_send_cnt + 1;
else
r_send_cnt <= r_send_cnt;
end
always@(posedge i_tx_clk,posedge i_tx_rst)
begin
if(i_tx_rst)
r_fifo_dout_little <= 'd0;
else if(r_fifo_rden)
r_fifo_dout_little <= w_fifo_dout_little;
else
r_fifo_dout_little <= r_fifo_dout_little;
end
always@(posedge i_tx_clk,posedge i_tx_rst)
begin
if(i_tx_rst)
r_axis_keep <= 'd0;
else if(s_axis_last)
r_axis_keep <= s_axis_keep;
else
r_axis_keep <= r_axis_keep;
end
always@(posedge i_tx_clk,posedge i_tx_rst)
begin
if(i_tx_rst)
ro_tx_data <= 'd0;
else if(r_send_cnt && r_input_end && ((r_axis_keep > 8'b1111_1100 && r_send_cnt == r_len - 0)
|| (r_axis_keep <= 8'b1111_1100 && r_send_cnt == r_len - 1)))
case(r_axis_keep)
8'b1111_1111:ro_tx_data <= {7'h16,7'h16,7'h16,7'h16,7'h16,7'h16,6'd0,r_fifo_dout_little[63:56],8'h99};//灏剧鍙戦��1涓�
8'b1111_1110:ro_tx_data <= {7'h16,7'h16,7'h16,7'h16,7'h16,7'h16,7'h16,7'h16,8'h8e};//灏剧鍙戦��8涓�
8'b1111_1100:ro_tx_data <= {w_fifo_dout_little[47:0],r_fifo_dout_little[63:56],8'hFF};//灏剧鍙戦��7涓�
8'b1111_1000:ro_tx_data <= {7'h16,1'd0,w_fifo_dout_little[39:0],r_fifo_dout_little[63:56],8'he8};//灏剧鍙戦��6涓�
8'b1111_0000:ro_tx_data <= {7'h16,7'h16,2'd0,w_fifo_dout_little[31:0],r_fifo_dout_little[63:56],8'hD4};
8'b1110_0000:ro_tx_data <= {7'h16,7'h16,7'h16,3'd0,w_fifo_dout_little[23:0],r_fifo_dout_little[63:56],8'hc3};
8'b1100_0000:ro_tx_data <= {7'h16,7'h16,7'h16,7'h16,4'd0,w_fifo_dout_little[15:0],r_fifo_dout_little[63:56],8'hB2};
8'b1000_0000:ro_tx_data <= {7'h16,7'h16,7'h16,7'h16,7'h16,5'd0,w_fifo_dout_little[7:0],r_fifo_dout_little[63:56],8'hA5};
endcase
else case(r_send_cnt)
0 :ro_tx_data <= {w_fifo_dout_little[55:0],8'h71};
default :ro_tx_data <= {w_fifo_dout_little[55:0],r_fifo_dout_little[63:56]};
endcase
end
always@(posedge i_tx_clk,posedge i_tx_rst)
begin
if(i_tx_rst)
ro_tx_header <= 'd0;
else if(r_send_cnt && r_input_end && ((r_axis_keep > 8'b1111_1100 && r_send_cnt == r_len - 0)
|| (r_axis_keep <= 8'b1111_1100 && r_send_cnt == r_len - 1)))
ro_tx_header <= 2'b10;
else if(r_send_cnt == 0 && r_fifo_rden_2d && r_invalid)
ro_tx_header <= 2'b10;
else if(r_send_cnt == 0 && r_fifo_rden_1d)
ro_tx_header <= 2'b10;
else
ro_tx_header <= 2'b01;
end
always@(posedge i_tx_clk,posedge i_tx_rst)
begin
if(i_tx_rst)
r_fifo_rden <= 'd0;
else if(w_fifo_empty || !w_gt_send_valid)
r_fifo_rden <= 'd0;
else if(!w_fifo_empty)
r_fifo_rden <= 'd1;
else
r_fifo_rden <= r_fifo_rden;
end
always@(posedge i_tx_clk,posedge i_tx_rst)
begin
if(i_tx_rst)
r_fifo_rden_1d <= 'd0;
else
r_fifo_rden_1d <= r_fifo_rden;
end
always@(posedge i_tx_clk,posedge i_tx_rst)
begin
if(i_tx_rst)
r_fifo_rden_2d <= 'd0;
else
r_fifo_rden_2d <= r_fifo_rden_1d;
end
always@(posedge i_tx_clk,posedge i_tx_rst)
begin
if(i_tx_rst)
r_fifo_rden_3d <= 'd0;
else
r_fifo_rden_3d <= r_fifo_rden_2d;
end
always@(posedge i_tx_clk,posedge i_tx_rst)
begin
if(i_tx_rst)
r_fifo_empty <= 'd0;
else
r_fifo_empty <= {r_fifo_empty[0],w_fifo_empty};
end
always@(posedge i_tx_clk,posedge i_tx_rst)
begin
if(i_tx_rst)
rs_axis_ready <= 'd1;
else if(s_axis_last)
rs_axis_ready <= 'd0;
else if(r_input_end && r_send_cnt == r_len - 0)
rs_axis_ready <= 'd1;
else
rs_axis_ready <= rs_axis_ready;
end
always@(posedge i_tx_clk,posedge i_tx_rst)
begin
if(i_tx_rst)
r_invalid <= 'd0;
else if(r_invalid && r_send_cnt)
r_invalid <= 'd0;
else if(r_fifo_rden && !w_gt_send_valid && r_send_cnt == 0)
r_invalid <= 'd1;
else
r_invalid <= r_invalid;
end
四、PHY RX
1、PHY RX Bitslip模块
该模块的主要作用是实现字节对齐功能。
这个模块的主要思想如下:
- 在64B/66B编码当中,只有2‘b10和2’b01代表有效的数据头,因此在字节对齐的过程中一直检测输入进来的数据头是否有效。
- 当检测到错误数据头时,通过计数器进行计数加一进行记录,当间隔一定时钟周期之后便会检测这些计数器,如果计数器不为0,则发送滑动信号,使对齐窗口滑动1个bit。
- 注意,间隔周期一定要大于32个RXUSERCLK2(Xilinx文档 UG476指定的最小间隔周期为32个RXUSERCLK2)
- 为了防止误判,可以设置只有当连续检测到64(如果出现误判可以再次翻倍)个正确的数据头部时,才会判定数据头已经对齐。
- 在Xilinx 例子工程的Block_syn模块中,在对其之后也会一直检测是否有错误的数据头,并对计数器进行复位,而本模块没有对计数器进行复位。
本模块字节对齐过程:
可以看到当字节对齐之后,无效头计数器的数值应该都为0,只有有效数据头计数器在一直增加。
挡在同步过程中检测到一个无效的数据头(黄色数据线的地方)便会重新启动字节同步
例子工程的块对齐模块:
可以看到在对其之后,begin信号会一直对计数器进行复位。
2、PHY RX模块
PHY RX模块的主要功能就是解帧以及大小端转换、字节对齐。
- 由于64B/66B编码判定传输开始和传输结束是通过不同的数据帧进行的,所以就要对接收到的数据帧进行解帧
- 在此过程中需要注意将GT IP传输过来的阿小端的数据转换为我们经常使用的大端的接口数据。
- 字节对齐,也就是在解帧的过程中要去除控制字符,并将接口的形式转换为AXI-Streaming接口的形式。在这里要特别注意EOF帧的在转换过程中Keep信号的处理。
在接收数据的时候需要注意,每隔32个时钟周期就有一个无效的数据发送过来,在设计的过程中需要考虑垓情况。尤其在刚接收到SOF帧后紧接着后面的无效数据,这种情况,要单独讨论,接收端的r_invalid便是用来处理此种情况。(这个需要后期在仔细思考)。
主要代码:
assign w_sof = ri_rx_header_valid & ri_rx_header == 2'b10 & ri_rx_data[7 :0] == 8'h71 & ri_rx_valid;
assign w_eof = ri_rx_header_valid & ri_rx_header == 2'b10 &
(ri_rx_data[7 :0] == 8'h99 ||
ri_rx_data[7 :0] == 8'h8e ||
ri_rx_data[7 :0] == 8'hff ||
ri_rx_data[7 :0] == 8'he8 ||
ri_rx_data[7 :0] == 8'hd4 ||
ri_rx_data[7 :0] == 8'hc3 ||
ri_rx_data[7 :0] == 8'hb2 ||
ri_rx_data[7 :0] == 8'ha5
)
& ri_rx_valid;
assign w_eof_s1 = i_rx_header_valid & i_rx_header == 2'b10 &
(i_rx_data[7 :0] == 8'h99 ||
i_rx_data[7 :0] == 8'h8e ||
i_rx_data[7 :0] == 8'hff ||
i_rx_data[7 :0] == 8'he8 ||
i_rx_data[7 :0] == 8'hd4 ||
i_rx_data[7 :0] == 8'hc3 ||
i_rx_data[7 :0] == 8'hb2 ||
i_rx_data[7 :0] == 8'ha5
)
& i_rx_valid;
assign w_eof_local = ri_rx_data[7 :0] == 8'h99 ? 1 :
ri_rx_data[7 :0] == 8'h8e ? 8 :
ri_rx_data[7 :0] == 8'hff ? 7 :
ri_rx_data[7 :0] == 8'he8 ? 6 :
ri_rx_data[7 :0] == 8'hd4 ? 5 :
ri_rx_data[7 :0] == 8'hc3 ? 4 :
ri_rx_data[7 :0] == 8'hb2 ? 3 :
ri_rx_data[7 :0] == 8'ha5 ? 2 :
'd0;
assign w_eof_local_s1 = i_rx_data[7 :0] == 8'h99 ? 1 :
i_rx_data[7 :0] == 8'h8e ? 8 :
i_rx_data[7 :0] == 8'hff ? 7 :
i_rx_data[7 :0] == 8'he8 ? 6 :
i_rx_data[7 :0] == 8'hd4 ? 5 :
i_rx_data[7 :0] == 8'hc3 ? 4 :
i_rx_data[7 :0] == 8'hb2 ? 3 :
i_rx_data[7 :0] == 8'ha5 ? 2 :
'd0;
always@(posedge i_rx_clk,posedge i_rx_rst)
begin
if(i_rx_rst) begin
ri_rx_data <= 'd0;
ri_rx_valid <= 'd0;
ri_rx_valid_1d <= 'd0;
ri_rx_header <= 'd0;
ri_rx_header_valid <= 'd0;
end else begin
ri_rx_data <= i_rx_data ;
ri_rx_valid <= i_rx_valid ;
ri_rx_valid_1d <= ri_rx_valid ;
ri_rx_header <= i_rx_header ;
ri_rx_header_valid <= i_rx_header_valid ;
end
end
always@(posedge i_rx_clk,posedge i_rx_rst)
begin
if(i_rx_rst)
ri_rx_data_1d <= 'd0;
else if(ri_rx_valid)
ri_rx_data_1d <= ri_rx_data ;
else
ri_rx_data_1d <= ri_rx_data_1d ;
end
always@(posedge i_rx_clk,posedge i_rx_rst)
begin
if(i_rx_rst) begin
r_sof <= 'd0;
r_eof <= 'd0;
r_eof_local <= 'd0;
end else begin
r_sof <= w_sof;
r_eof <= w_eof;
r_eof_local <= w_eof_local;
end
end
always@(posedge i_rx_clk,posedge i_rx_rst)
begin
if(i_rx_rst)
r_receiving <= 'd0;
else if(r_eof)
r_receiving <= 'd0;
else if(w_sof)
r_receiving <= 'd1;
else
r_receiving <= r_receiving;
end
// always@(posedge i_rx_clk,posedge i_rx_rst)
// begin
// if(i_rx_rst)
// rm_axis_data <= 'd0;
// else if(r_receiving)
// s
// else
// rm_axis_data <= rm_axis_data;
// end
always@(posedge i_rx_clk,posedge i_rx_rst)
begin
if(i_rx_rst)
rm_axis_data <= 'd0;
else if(r_eof && r_eof_local < 8)
rm_axis_data <= {ri_rx_data_1d[63:16]};
else if(w_eof && w_eof_local < 8)
rm_axis_data <= {ri_rx_data[15:8],ri_rx_data_1d[63:8]};
else if(w_eof && (w_eof_local == 8 || w_eof_local == 1))
rm_axis_data <= {ri_rx_data[7 :0],ri_rx_data_1d[63:8]};
else if(r_receiving && ri_rx_valid)
rm_axis_data <= {ri_rx_data[7 :0],ri_rx_data_1d[63:8]};
else
rm_axis_data <= 'd0;
end
always@(posedge i_rx_clk,posedge i_rx_rst)
begin
if(i_rx_rst)
rm_axis_keep <= 8'b1111_1111;
else if(r_eof && (r_eof_local >1 && r_eof_local < 8))
case(r_eof_local)
1 :rm_axis_keep <= 8'b1111_1111;
2 :rm_axis_keep <= 8'b1000_0000;
3 :rm_axis_keep <= 8'b1100_0000;
4 :rm_axis_keep <= 8'b1110_0000;
5 :rm_axis_keep <= 8'b1111_0000;
6 :rm_axis_keep <= 8'b1111_1000;
7 :rm_axis_keep <= 8'b1111_1100;
8 :rm_axis_keep <= 8'b1111_1110;
default :rm_axis_keep <= 8'b1111_1111;
endcase
else if(w_eof && (w_eof_local == 8 || w_eof_local == 1))
case(w_eof_local)
1 :rm_axis_keep <= 8'b1111_1111;
2 :rm_axis_keep <= 8'b1000_0000;
3 :rm_axis_keep <= 8'b1100_0000;
4 :rm_axis_keep <= 8'b1110_0000;
5 :rm_axis_keep <= 8'b1111_0000;
6 :rm_axis_keep <= 8'b1111_1000;
7 :rm_axis_keep <= 8'b1111_1100;
8 :rm_axis_keep <= 8'b1111_1110;
default :rm_axis_keep <= 8'b1111_1111;
endcase
// else if(w_eof_s1 && w_eof_local_s1 == 8)
// rm_axis_keep <= 8'b1111_1111;
else
rm_axis_keep <= 8'b1111_1111;
end
always@(posedge i_rx_clk,posedge i_rx_rst)
begin
if(i_rx_rst)
rm_axis_last <= 'd0;
else if(rm_axis_last && rm_axis_valid)
rm_axis_last <= 'd0;
else if(rm_axis_valid && r_eof && (r_eof_local >1 && r_eof_local < 8))
rm_axis_last <= 'd1;
else if(rm_axis_valid && w_eof && (w_eof_local == 8 || w_eof_local == 1))
rm_axis_last <= 'd1;
else
rm_axis_last <= rm_axis_last;
end
always@(posedge i_rx_clk,posedge i_rx_rst)
begin
if(i_rx_rst)
rm_axis_valid <= 'd0;
else if(r_sof)
rm_axis_valid <= 'd1;
else if(rm_axis_last && rm_axis_valid)
rm_axis_valid <= 'd0;
else if((!ri_rx_valid && ri_rx_header != 2'b10) || r_invalid)
rm_axis_valid <= 'd0;
else if(r_revalid)
rm_axis_valid <= 'd1;
else
rm_axis_valid <= rm_axis_valid;
end
always@(posedge i_rx_clk,posedge i_rx_rst)
begin
if(i_rx_rst)
r_revalid <= 'd0;
else if(r_invalid)
r_revalid <= 'd1;
else if(!rm_axis_last && rm_axis_valid && !ri_rx_valid && ri_rx_valid_1d)
r_revalid <= 'd1;
else
r_revalid <= 'd0;
end
always@(posedge i_rx_clk,posedge i_rx_rst)
begin
if(i_rx_rst)
r_invalid <= 'd0;
else if(r_sof & !ri_rx_valid)
r_invalid <= 'd1;
else
r_invalid <= 'd0;
end
五、上板测试
上板测试如下,GT 放置两个通道,两个通道之间互相发送和接收数据。
六、总结
商用的PHY芯片肯定比这些要复杂的多,我们在这边只要了解64B/66B编码的原因,以及PHY的原理和处理过程、思想就好了。