基于GTX 8B10B编码的自定义PHY接收模块(高速收发器十三)

  点击进入高速收发器系列文章导航界面


  前文完成了发送模块的设计,本文接着完成接收模块的设计,接收模块相对发送模块会更加麻烦。

1、设计思路

  前文在讲解官方示例工程时,提到GTX IP的接收部分没有做字对齐,需要用户自己编写字对齐模块,由于发送端可能发送任意字节数据,因此停止位也可能出现在一帧数据的任意位置。

  下图是自定义的帧格式,在实际收发数据过程中,部分逗号可能被用于时钟纠正。在检测数据帧时,将24’hbc50fb作为一帧数据的帧头,帧尾依旧是8’hfd。

在这里插入图片描述

图1 帧格式

  由于GTX IP的接收通道没有进行字对齐,接收的帧头和帧尾的数据可能会出现下图中的16种现象。

在这里插入图片描述

图2 帧头帧尾的分布

  起始位8’hfb可能出现在一个数据的任何字节位置,停止位8’hfd也可能出现在一个数据的任意字节位置(因为发送端可能发送任意字节数据)。这些条件会使得输出给用户的最后一个数据的部分数据无效,需要用掩码进行控制。

  蓝色数字用于记录起始位和停止位在所处数据中哪个字节,这与最后一个数据的掩码信号直接相关。

  黄色的掩码表示这个数据会出现在下一个数据中,与停止位对齐的数据都i是有效的,由三种情况出现这种情况。

  可以对图2中的规律进行总结,首先当停止位所在字节数减去起始位所在字节数等于1时,输出给用户的最后一个数据所有字节均有效。

  当起始位所在位置与停止位所在位置相差2时,输出给用户的最后一个数据只有最高字节有效。

  当停止位所在字节数减去起始位所在字节数等于3 或者 起始位所在字节数减去停止位所在字节数等于1时,输出给用户的最后一个数据高两字节数据均有效。

  当起始位和停止位所在的位置相等时,输出给用户最后一个数据的高三字节均有效。

  本次主要思路是使用移位寄存器对GTX IP输出数据进行暂存,然后检测帧头,并且确定起始位和停止位在数据中的位置,便于后续生成数据掩码信号。

  当检测到帧头之后,就可以根据起始位所在位置向用户输出数据了,由于模块内部没有存储结构,因此数据信号不需要等待用户的应答信号。

  本模块设计的难点在于一帧数据的最后一个数据的掩码信号和最后一个数据有效指示信号的生成,其余信号都比较简单。

2、代码讲解

  本模块并没有使用计数器、状态机、FIFO等结构,仅仅以输出数据和K码指示信号的移位寄存器作为主体,完成设计。

  首先是端口部分,包括GTX IP输出端口的输出数据、K码指示信号、字节对齐指示信号,用户端口是去掉数据应答信号的axi_stream流端口,如下所示。

module phy_rx (input									clk		        ,//系统时钟信号;input									rst 	        ,//系统复位信号,高电平有效;output  reg         [31 : 0]            axi_s_data      ,//输出接收的数据,先输出高字节数据;output  reg         [3 : 0]             axi_s_keep      ,//输出接收数据有效指示信号;output  reg                             axi_s_last      ,//输出一帧数据的最后一个数据指示信号;output  reg                             axi_s_valid     ,//输出数据有效指示信号;input                                   gt_rx_bytealign ,//输入数据字节对齐指示信号;input               [31 : 0]            gt_rx_data      ,//输入待解析的数据;input               [3 : 0]             gt_rx_char       //输入数据K码指示信号,高电平有效;
);

  下面是移位寄存器,对GTX IP输出的数据和K码指示信号暂存,注意在写移位寄存器的时候,尽量不要写复位相关的代码,因为移位寄存器不需要复位。

    //将接收的数据和K码指示信号暂存,用于后续处理。always@(posedge clk)begin{gt_rx_char_r[2],gt_rx_char_r[1],gt_rx_char_r[0]} <= {gt_rx_char_r[1],gt_rx_char_r[0],gt_rx_char};{gt_rx_data_r[2],gt_rx_data_r[1],gt_rx_data_r[0]} <= {gt_rx_data_r[1],gt_rx_data_r[0],gt_rx_data};eof_flag_r <= {eof_flag_r[0],eof_flag};end

  下面检测图2中可能出现进行四种帧头的数据格式,检测到帧头后把commoa_flag拉高一个时钟,与gt_rx_data_r[1]对齐。

    //当检测到24'hBC50FB时,表示检测到同步码且检测到起始位,同步标志信号与gt_rx_data_r[1]对齐。//为了后续方便生成keep信号,只能将数据打两拍在进行检测。always@(posedge clk)beginif(rst)begin//初始值为0;commoa_flag <= 1'b0;end//检测到的序列是32'h50bc50bc,32'hXXXXXXfb时拉高,表示检测到同步码。else if((gt_rx_data_r[1][31:16] == 16'h50bc && gt_rx_char_r[1][3:2] == 2'b01) && (gt_rx_data_r[0][7:0] == 8'hfb && gt_rx_char_r[0][0] == 1'b1))begincommoa_flag <= 1'b1;end//检测到的序列是32'hBC50BCXX,32'hXXXXfb50时拉高,表示检测到同步码。else if((gt_rx_data_r[1][31:24] == 8'hbc && gt_rx_char_r[1][3] == 1'b1) && (gt_rx_data_r[0][15:0] == 16'hfb50 && gt_rx_char_r[0][1:0] == 2'b10))begincommoa_flag <= 1'b1;end//检测到的序列是32'h50BCXXXX,32'hXXfb50BC时拉高,表示检测到同步码。else if(gt_rx_data_r[0][23:0] == 24'hfb50bc && gt_rx_char_r[0][2:0] == 3'b101)begincommoa_flag <= 1'b1;end//检测到的序列是32'hBCXXXXXX,32'hfb50BC50时拉高,表示检测到同步码。else if(gt_rx_data_r[0][31:8] == 24'hfb50bc && gt_rx_char_r[0][3:1] == 3'b101)begincommoa_flag <= 1'b1;endelse begincommoa_flag <= 1'b0;endend

  检测到帧头后,将接收数据标志信号(rx_data_flag)拉高,直到接收完一帧数据后拉低。

    //接收数据标志信号,与gt_rx_data_r[2]对齐。always@(posedge clk)beginif(rst)begin//初始值为0;rx_data_flag <= 1'b0;endelse if(axi_s_last)begin//当检测到停止位时拉低。rx_data_flag <= 1'b0;endelse if(commoa_flag && gt_rx_bytealign)begin//当检测到同步码和起始位后拉高,表示一帧数据的开始。rx_data_flag <= 1'b1;endend

  并且在检测到帧头后,需要记录起始位位于数据中的字节数。

    //检测到起始位标志信号,以及标志信号位于所在数据的字节数,与gt_rx_data_r[2]对齐。always@(posedge clk)beginif(rst)begin//初始值为0;sof_local <= 3'd0;endelse if(commoa_flag && gt_rx_bytealign)begin//检测到同步码和起始位时,记录起始位位于该数据的字节位置。if((gt_rx_data_r[1][7:0] == 8'hfb) && gt_rx_char_r[1][0])sof_local <= 3'd0;else if((gt_rx_data_r[1][15:8] == 8'hfb) && gt_rx_char_r[1][1])sof_local <= 3'd1;else if((gt_rx_data_r[1][23:16] == 8'hfb) && gt_rx_char_r[1][2])sof_local <= 3'd2;else if((gt_rx_data_r[1][31:24] == 8'hfb) && gt_rx_char_r[1][3])sof_local <= 3'd3;endend

  之后需要检测一帧数据的帧尾8’hfd,并且记录停止位8’hfd在该数据中的字节数。

    //检测到停止位标志信号,以及记录停止位位于该数据的字节位置。always@(posedge clk)beginif(rst)begin//初始值为0;eof_flag <= 1'b0;eof_local <= 3'd0;endelse if(rx_data_flag)begin//当接收数据时,检测停止位的位置。if((gt_rx_data[7:0] == 8'hfd) && gt_rx_char[0])begin//检测到停止位位于最低字节。eof_flag <= 1'b1;eof_local <= 3'd0;endelse if((gt_rx_data[15:8] == 8'hfd) && gt_rx_char[1])begineof_flag <= 1'b1;eof_local <= 3'd1;endelse if((gt_rx_data[23:16] == 8'hfd) && gt_rx_char[2])begineof_flag <= 1'b1;eof_local <= 3'd2;endelse if((gt_rx_data[31:24] == 8'hfd) && gt_rx_char[3])begin//检测到停止位位于最高字节。eof_flag <= 1'b1;eof_local <= 3'd3;endelse begineof_flag <= 1'b0;endendelse begineof_flag <= 1'b0;endend

  当处于接收数据状态(rx_data_flag为高电平)时,根据起始位所在字节数,把接收的前后两字节数据拼接成输出数据。

  注意GTX IP输出数据是小端对齐,而输出给用户的数据是大端对齐,此处在输出数据时需要进行调换。比如起始位位于最高字节时,直接将gt_rx_data_r[1]的高低字节顺序调换输出即可。

    //生成输出数据,由于输出数据是先输出高字节数据,而接收的数据是先发送低字节数据,所以在拼接时需要进行纠正处理。always@(posedge clk)beginif(rst)begin//初始值为0;axi_s_data <= 32'd0;endelse if(rx_data_flag)begincase (sof_local)//rx_data_flag与gt_rx_data_r[2]对齐,因此赋值时直接使用gt_rx_data_r[2]的信号。2'd0 : axi_s_data <= {gt_rx_data_r[2][15:8],gt_rx_data_r[2][23:16],gt_rx_data_r[2][31:24],gt_rx_data_r[1][7:0]};//将先接收的数据放在高字节处进行输出。2'd1 : axi_s_data <= {gt_rx_data_r[2][23:16],gt_rx_data_r[2][31:24],gt_rx_data_r[1][7:0],gt_rx_data_r[1][15:8]};//将先接收的数据放在高字节处进行输出。2'd2 : axi_s_data <= {gt_rx_data_r[2][31:24],gt_rx_data_r[1][7:0],gt_rx_data_r[1][15:8],gt_rx_data_r[1][23:16]};//将先接收的数据放在高字节处进行输出。2'd3 : axi_s_data <= {gt_rx_data_r[1][7:0],gt_rx_data_r[1][15:8],gt_rx_data_r[1][23:16],gt_rx_data_r[1][31:24]};//将先接收的数据放在高字节处进行输出。default : ;endcaseendend

  至于数据掩码信号,通过两个always模块生成,第一个模块通过图2描述的方式生成最后一个数据的掩码信号。

    //计算最后一个输出数据的掩码信号;always@(posedge clk)beginif(rst)begin//初始值为0;axi_s_keep_r <= 4'hf;endelse if(eof_flag)begin//结束标志有效时,通过起始位和停止位的位置,计算最后一个数据的有效字节位置。if(eof_local - sof_local == 3'd1)beginaxi_s_keep_r <= 4'b1111;endelse if((eof_local - sof_local == 3'd2) || (sof_local - eof_local == 3'd2))beginaxi_s_keep_r <= 4'b1000;endelse if((eof_local - sof_local == 3'd3) || (sof_local - eof_local == 3'd1))beginaxi_s_keep_r <= 4'b1100;endelse if(eof_local == sof_local)beginaxi_s_keep_r <= 4'b1110;endendend

  但是部分掩码信号应该作用在后一个输出数据(图2中黄色部分掩码)。因此需要第二个always确定何时输出最后一个数据的掩码。

    //生成数据掩码信号;always@(posedge clk)beginif(rst)begin//初始值为4'hf;axi_s_keep <= 4'hf;end//当最后一个数据刚好在停止位所在数据时,将计算结果赋值输出。else if(((sof_local > 1) || ((sof_local <= 1) && (((eof_local - sof_local < 2) && (eof_local >= sof_local)) || (sof_local > eof_local)))) && eof_flag_r[0])beginaxi_s_keep <= axi_s_keep_r;endelse if((((sof_local <= 1) &&  (eof_local > sof_local) && (eof_local - sof_local >= 2))) && eof_flag_r[1])beginaxi_s_keep <= axi_s_keep_r;endelse begin//其余时间输出的数据均有效。axi_s_keep <= 4'hf;endend

  之后是一帧数据的结尾数据的指示信号,这个信号与K码指示信号类似,也是根据图二生成的信号,对应代码如下所示。

    //生成输出最后一个数据的指示信号;always@(posedge clk)beginif(rst)begin//初始值为0;axi_s_last <= 1'b0;endelse if(sof_local > 1)beginif((sof_local == 3) && (eof_local == 0))//axi_s_last <= eof_flag;elseaxi_s_last <= eof_flag_r[0];endelse if(((eof_local - sof_local < 2) && (eof_local >= sof_local)) || (sof_local > eof_local))beginaxi_s_last <= eof_flag_r[0];endelse if((eof_local > sof_local) && (eof_local - sof_local >= 2))beginaxi_s_last <= eof_flag_r[1];endelse beginaxi_s_last <= 1'b0;endend

  最后是输出数据有效指示信号,接收数据后一直为高电平,直到向用户输出最后一个数据后拉低。

    //生成数据有效指示信号;always@(posedge clk)beginif(rst)begin//初始值为0;axi_s_valid <= 1'b0;endelse if(axi_s_last)begin//接收完数据时拉低。axi_s_valid <= 1'b0;endelse if(rx_data_flag)begin//开始接收数据时拉高。axi_s_valid <= 1'b1;endend

  当理解了图2后,整个设计其实难度也比较小,主要的就是掩码信号和最后数据指示信号的生成,以及一些信号之间的对齐关系。

3、模块仿真

  将PHY发送和接收模块例化形成PHY顶层模块,对应代码如下所示。

//--###############################################################################################
//--#
//--# File Name		: pdy_module
//--# Designer		: 数字站
//--# Tool			: Vivado 2021.1
//--# Design Date	: 2024.3.17
//--# Description	: 例化PHY的收发模块。
//--# Version		: 0.0
//--# Coding scheme	: GBK(If the Chinese comment of the file is garbled, please do not save it and check whether the file is opened in GBK encoding mode)
//--#
//--###############################################################################################
module phy_module (input               i_tx_clk        ,//发送部分时钟信号;input               i_tx_rst        ,//发送部分复位信号,高电平有效;input               i_rx_clk        ,//接收部分时钟信号;input               i_rx_rst        ,//接收模块复位信号,高电平有效;input  [31:0]       i_axi_s_data    ,//AXI的数据输入信号;input  [3 :0]       i_axi_s_keep    ,//AXI的数据掩码信号,低电平有效;input               i_axi_s_last    ,//AXI输入最后一个数据指示信号;input               i_axi_s_valid   ,//AXI输入数据有效指示信号;output              o_axi_s_ready   ,//AXI输入数据应答信号;output [31:0]       o_axi_s_data    ,//输出接收的数据,先输出高字节数据;output [3 :0]       o_axi_s_keep    ,//输出接收数据有效指示信号;output              o_axi_s_last    ,//输出一帧数据的最后一个数据指示信号;output              o_axi_s_valid   ,//输出数据有效指示信号;input               gt_tx_done      ,//GTX发送部分初始化成功,高电平有效;output [31:0]       gt_tx_data      ,//GTX发送数据;output [3 :0]       gt_tx_char      ,//GTX发送数据K码指示信号,高电平有效;input               gt_rx_bytealign ,//输入数据字节对齐指示信号;input  [31:0]       gt_rx_data      ,//输入待解析的数据;input  [3 :0]       gt_rx_char       //输入数据K码指示信号,高电平有效;
);//例化PHY芯片的发送模块。phy_tx u_phy_tx(.clk		    ( i_tx_clk      ),//发送部分时钟信号;.rst 	        ( i_tx_rst      ),//发送部分复位信号,低电平有效;.axi_s_data     ( i_axi_s_data  ),//AXI的数据输入信号;.axi_s_keep     ( i_axi_s_keep  ),//AXI的数据掩码信号,低电平有效;.axi_s_last     ( i_axi_s_last  ),//AXI输入最后一个数据指示信号;.axi_s_valid    ( i_axi_s_valid ),//AXI输入数据有效指示信号;.axi_s_ready    ( o_axi_s_ready ),//AXI输入数据应答信号;.gt_tx_done     ( gt_tx_done    ),//GTX发送部分初始化成功,高电平有效;.gt_tx_data     ( gt_tx_data    ),//GTX发送数据;.gt_tx_char     ( gt_tx_char    ) //GTX发送数据K码指示信号,高电平有效;);//例化PHY芯片的接收模块。phy_rx u_phy_rx(.clk		    ( i_rx_clk          ),//接收部分时钟信号;.rst 	        ( i_rx_rst          ),//接收模块复位信号,低电平有效;.axi_s_data     ( o_axi_s_data      ),//输出接收的数据,先输出高字节数据;.axi_s_keep     ( o_axi_s_keep      ),//输出接收数据有效指示信号;.axi_s_last     ( o_axi_s_last      ),//输出一帧数据的最后一个数据指示信号;.axi_s_valid    ( o_axi_s_valid     ),//输出数据有效指示信号;.gt_rx_bytealign( gt_rx_bytealign   ),//输入数据字节对齐指示信号;.gt_rx_data     ( gt_rx_data        ),//输入待解析的数据;.gt_rx_char     ( gt_rx_char        ) //输入数据K码指示信号,高电平有效;);endmodule

  PHY顶层模块的TestBench如下所示,可以通过修改TX_KEEP和SOF_LOCAL来确定接收模块接收数据的起始位和停止位的位置,进而验证图2中16种数据格式。

//--###############################################################################################
//--#
//--# File Name		: tb_phy_module
//--# Designer		: 数字站
//--# Tool			: Vivado 2021.1
//--# Design Date	: 2024.3.17
//--# Description	: TestBench
//--# Version		: 0.0
//--# Coding scheme	: GBK(If the Chinese comment of the file is garbled, please do not save it and check whether the file is opened in GBK encoding mode)
//--#
//--###############################################################################################
`timescale 1 ns/1 ns
module tb_phy_module();localparam	CYCLE		= 	10		        ;//系统时钟周期,单位ns,默认10ns;localparam	RST_TIME	= 	10		        ;//系统复位持续时间,默认10个系统时钟周期;localparam  TX_KEEP     =   4'b1111         ;//发送最后一个数据的有效位数,大端对齐;localparam  SOF_LOCAL   =   0               ;//起始位位于第几字节。reg			                clk             ;//系统时钟,默认100MHz;reg			                rst_n           ;//系统复位,默认低电平有效;reg         [7 : 0]         send_value      ;reg         [31 : 0]        i_axi_s_data    ;reg         [3 : 0]         i_axi_s_keep    ;reg                         i_axi_s_last    ;reg                         i_axi_s_valid   ;wire                        o_axi_s_ready   ;wire        [31 : 0]        gt_tx_data      ;wire        [3  : 0]        gt_tx_char      ;reg [3 : 0]  gt_tx_char_r;reg [31 : 0] gt_tx_data_r;//wire [31: 0]    gt_tx_data_o    ;wire [3 : 0]    gt_tx_char_o    ;phy_module u_phy_module(.i_tx_clk       ( clk           ),//发送部分时钟信号;.i_tx_rst_n     ( rst_n         ),//发送部分复位信号,低电平有效;.i_rx_clk       ( clk           ),//接收部分时钟信号;.i_rx_rst_n     ( rst_n         ),//发送模块复位信号,低电平有效;.i_axi_s_data   ( i_axi_s_data  ),//AXI的数据输入信号;.i_axi_s_keep   ( i_axi_s_keep  ),//AXI的数据掩码信号,低电平有效;.i_axi_s_last   ( i_axi_s_last  ),//AXI输入最后一个数据指示信号;.i_axi_s_valid  ( i_axi_s_valid ),//AXI输入数据有效指示信号;.o_axi_s_ready  ( o_axi_s_ready ),//AXI输入数据应答信号;.o_axi_s_data   (               ),//输出接收的数据,先输出高字节数据;.o_axi_s_keep   (               ),//输出接收数据有效指示信号;.o_axi_s_last   (               ),//输出一帧数据的最后一个数据指示信号;.o_axi_s_valid  (               ),//输出数据有效指示信号;.gt_tx_done     ( 1'b1          ),//GTX发送部分初始化成功,高电平有效;.gt_tx_data     ( gt_tx_data    ),//GTX发送数据;.gt_tx_char     ( gt_tx_char    ),//GTX发送数据K码指示信号,高电平有效;.gt_rx_bytealign( 1'b1          ),//输入数据字节对齐指示信号;.gt_rx_data     ( gt_tx_data_o  ),//输入待解析的数据;.gt_rx_char     ( gt_tx_char_o  ) //输入数据K码指示信号,高电平有效;);always@(posedge clk)begingt_tx_data_r <= gt_tx_data;gt_tx_char_r <= gt_tx_char;end//根据起始位的位置插入不同的数据。generateif(SOF_LOCAL == 0)beginassign gt_tx_data_o = gt_tx_data_r;assign gt_tx_char_o = gt_tx_char_r;endelse if(SOF_LOCAL == 1)beginassign gt_tx_data_o = {gt_tx_data[23:0],gt_tx_data_r[31:24]};assign gt_tx_char_o = {gt_tx_char[2:0],gt_tx_char_r[3]};endelse if(SOF_LOCAL == 2)beginassign gt_tx_data_o = {gt_tx_data[15:0],gt_tx_data_r[31:16]};assign gt_tx_char_o = {gt_tx_char[1:0],gt_tx_char_r[3:2]};endelse if(SOF_LOCAL == 3)beginassign gt_tx_data_o = {gt_tx_data[7:0],gt_tx_data_r[31:8]};assign gt_tx_char_o = {gt_tx_char[0],gt_tx_char_r[3:1]};endendgenerate//生成周期为CYCLE数值的系统时钟;initial beginclk = 0;forever #(CYCLE/2) clk = ~clk;end//生成复位信号;initial beginrst_n = 1;#2;rst_n = 0;//开始时复位10个时钟;#(RST_TIME*CYCLE);rst_n = 1;end//生成输入信号din;initial begini_axi_s_data  = 32'd0;i_axi_s_keep  = 4'd0;i_axi_s_last  = 1'd0;i_axi_s_valid = 1'd0;wait(rst_n);//等待复位完成;repeat(10) @(posedge clk);forever beginphy_tx_task(21);endend//发送数据的任务;task phy_tx_task(input	[7 : 0]		len);begin : phy_tx_task_0integer i;i_axi_s_data  <= 32'd0;i_axi_s_keep  <= 4'd0;i_axi_s_last  <= 1'd0;i_axi_s_valid <= 1'd0;send_value <= 8'd1;@(posedge clk);wait(o_axi_s_ready);for(i=0 ; i<len ; i=i+1)beginsend_value <= send_value + 1;i_axi_s_data <= {send_value,send_value,send_value,send_value};if(i == len - 1)begini_axi_s_last <= 1'b1;i_axi_s_keep <= TX_KEEP;endelse begini_axi_s_last <= 1'b0;i_axi_s_keep <= 4'hf;endi_axi_s_valid <= 1'b1;@(posedge clk);endi_axi_s_data  <= 32'd0;i_axi_s_keep  <= 4'd0;i_axi_s_last  <= 1'd0;i_axi_s_valid <= 1'd0;@(posedge clk);endendtaskendmodule

  如下图示,起始位和停止位均处于最低字节,输出给用户的最后一个数据里高三字节数据均有效。

在这里插入图片描述

图3 起始位和停止位均处于最低字节

  如下图示,起始位位于最低字节,停止位位于第1字节,那么输出给用户的最后一个数据的所有位均有效,仿真结果正确。

在这里插入图片描述

图4 起始位位于第0字节,停止位位于第1字节

  如下图示,起始位位于最低字节,停止位位于第2字节,那么输出给用户的最后一个数据的只有最高字节有效,仿真结果正确。

在这里插入图片描述

图5 起始位位于第0字节,停止位位于第2字节

  如下图示,起始位位于最低字节,停止位位于第3字节,那么输出给用户的最后一个数据的高两字节均有效,仿真结果正确。

在这里插入图片描述

图6 起始位位于第0字节,停止位位于第3字节

  如下图示,起始位位于第1字节,停止位位于第0字节,那么输出给用户的最后一个数据的高两字节均有效,仿真结果正确。

在这里插入图片描述

图7 起始位位于第1字节,停止位位于第0字节

  如下图示,起始位位于第1字节,停止位位于第1字节,那么输出给用户的最后一个数据的高三字节均有效,仿真结果正确。

在这里插入图片描述

图8 起始位位于第1字节,停止位位于第1字节

  如下图示,起始位位于第1字节,停止位位于第2字节,那么输出给用户的最后一个数据的所有字节均有效,仿真结果正确。

在这里插入图片描述

图9 起始位位于第1字节,停止位位于第2字节

  如下图示,起始位位于第1字节,停止位位于第3字节,那么输出给用户的最后一个数据只有最高字节有效,仿真结果正确。

在这里插入图片描述

图10 起始位位于第1字节,停止位位于第3字节

  如下图示,起始位位于第2字节,停止位位于第0字节,那么输出给用户的最后一个数据只有最高字节有效,仿真结果正确。

在这里插入图片描述

图11 起始位位于第2字节,停止位位于第0字节

  如下图示,起始位位于第2字节,停止位位于第1字节,那么输出给用户的最后一个数据只有最高两字节有效,仿真结果正确。

在这里插入图片描述

图12 起始位位于第2字节,停止位位于第1字节

  如下图示,起始位位于第2字节,停止位位于第2字节,那么输出给用户的最后一个数据只有高三字节均有效,仿真结果正确。

在这里插入图片描述

图13 起始位位于第2字节,停止位位于第2字节

  如下图示,起始位位于第2字节,停止位位于第3字节,那么输出给用户的最后一个数据所有字节均有效,仿真结果正确。

在这里插入图片描述

图14 起始位位于第2字节,停止位位于第3字节

  如下图示,起始位位于第3字节,停止位位于第0字节,那么输出给用户的最后一个数据所有字节均有效,仿真结果正确。

在这里插入图片描述

图15 起始位位于第3字节,停止位位于第0字节

  如下图示,起始位位于第3字节,停止位位于第1字节,那么输出给用户的最后一个数据只有最高字节数据有效,仿真结果正确。

在这里插入图片描述

图16 起始位位于第3字节,停止位位于第1字节

  如下图示,起始位位于第3字节,停止位位于第2字节,那么输出给用户的最后一个数据的高两字节数据均有效,仿真结果正确。

在这里插入图片描述

图17 起始位位于第3字节,停止位位于第2字节

  如下图示,起始位位于第3字节,停止位位于第3字节,那么输出给用户的最后一个数据的高三字节数据均有效,仿真结果正确。

在这里插入图片描述

图18 起始位位于第3字节,停止位位于第3字节

  仿真到此结束,通过修改TestBench中的TX_KEEP和SOF_LOCAL参数,完成了十六种接收格式的仿真,输出给用户的数据均没有问题,仿真结束。

  本模块主要完成输出数据的检测、字对齐、掩码信号的生成,支持接收发送端的任意字节数据。

  PHY顶层模块对应的RTL视图如下所示,由于高速收发器是全双工通信,因此把发送和接收分为两个模块。

在这里插入图片描述

图19 PHY顶层模块

  关于自定义PHY接收模块的设计到此结束了,下文将GT收发器模块、自定义PH发送模块、自定义PHY接收模块综合,然后通过光口对协议进行验证。


  如果对文章内容理解有疑惑或者对代码不理解,可以在评论区或者后台留言,看到后均会回复!

  如果本文对您有帮助,还请多多点赞👍、评论💬和收藏⭐!您的支持是我更新的最大动力!将持续更新工程!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/339337.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

---初始Linux---

一、认识计算机 计算机 硬件 软件 硬件&#xff1a;就是计算机系统中由电子、机械和光电元件等组成的各种物理装置的总称&#xff08;CPU\GPU\...&#xff09; 软件&#xff1a;是用户和计算机硬件之间及进行交流的工具 然而一个简单的计算机或者说基本的计算机就是有两大…

Android开机动画,framework修改Bootanimation绘制文字。

文章目录 Android开机动画&#xff0c;framework修改Bootanimation动画绘制文字。 Android开机动画&#xff0c;framework修改Bootanimation动画绘制文字。 frameworks/base/cmds/bootanimation/bootanimation.cpp 绘制时间的一个方法 // We render 12 or 24 hour time. void…

Linux 僵尸进程和孤儿进程

一.Z(zombie)-僵尸进程 1.僵死状态&#xff08;Zombies&#xff09;是一个比较特殊的状态。当进程退出并且父进程&#xff08;使用wait()系统调用后&#xff09;没有读取到子进程退出的返回代码时就会产生僵死(尸)进程 2.僵死进程会以终止状态保持在进程表中&#xff0c;并且会…

Spring 中如何控制 Bean 的加载顺序?

如果你脱口而出说添加 Order 注解或者是实现 Ordered 接口&#xff0c;那么恭喜&#xff0c;你掉坑了。 一 Order 注解和 Ordered 接口 在 Spring 框架中&#xff0c;Order 是一个非常实用的元注解&#xff0c;它位于 spring-core 包下&#xff0c;主要用于控制某些特定上下文…

SQL实验 带函数查询和综合查询

一、实验目的 1&#xff0e;掌握Management Studio的使用。 2&#xff0e;掌握带函数查询和综合查询的使用。 二、实验内容及要求 1&#xff0e;统计年龄大于30岁的学生的人数。 --统计年龄大于30岁的学生的人数。SELECT COUNT(*) AS 人数FROM StudentWHERE (datepart(yea…

小公司的软件开发IT工具箱

目录 工具链困境 难题的解决 达到的效果 资源要求低 工具箱一览 1、代码管理工具 2、自动化发版&#xff08;测试&#xff09;工具 3、依赖库&#xff08;制品包&#xff09;管理 4、镜像管理 5、授权管理&#xff08;可选&#xff09; 待讨论&#xff1a;为什么不是…

基于全志T507-H的Linux-RT实时性测试案例分享

本文将为各位工程师演示全志T507-H工业评估板&#xff08;TLT507-EVM&#xff09;基于IgH EtherCAT控制伺服电机方法&#xff0c;生动说明Linux-RT Igh EtherCAT的强大之处&#xff01; Linux-RT系统的优势 内核开源、免费、功能完善。 RT PREEMPT补丁&#xff0c;使Linux内…

【Qt】对话框

文章目录 1 :peach:对话框介绍:peach:2 :peach:对话框的分类:peach:2.1 :apple:模态对话框:apple:2.2 :apple:非模态对话框:apple:2.3 :apple:混合属性对话框:apple: 3 :peach:Qt 内置对话框:peach:3.1 :apple:消息对话框 QMessageBox:apple: 1 &#x1f351;对话框介绍&#x…

AK F.*ing leetcode 流浪计划之费马小定理与组合数取模

欢迎关注更多精彩 关注我&#xff0c;学习常用算法与数据结构&#xff0c;一题多解&#xff0c;降维打击。 费马小定理与证明 参考 https://zhuanlan.zhihu.com/p/594859227 费马小定理&#xff1a;如果p是一个质数&#xff0c;而正整数a不是p的倍数&#xff0c;那么a(p-1)≡…

LabVIEW齿轮调制故障检测系统

LabVIEW齿轮调制故障检测系统 概述 开发了一种基于LabVIEW平台的齿轮调制故障检测系统&#xff0c;实现齿轮在恶劣工作条件下的故障振动信号的实时在线检测。系统利用LabVIEW的强大图形编程能力&#xff0c;结合Hilbert包络解调技术&#xff0c;对齿轮的振动信号进行精确分析…

opensips 3.5的DB部署

opensips 3.X的DB部署方式较之前版本有很大的不同。本文以opensips 3.5 为例&#xff0c;说明部署的过程。 当OpenSIPS安装完成后&#xff0c;需要进一步做什么&#xff1f;最大的可能就是部署配套的DB。因为很多功能离不开它&#xff0c;比如用户鉴权、注册信息持久化、dialog…

MySQL学习——影响选项文件处理的命令行选项和程序选项修改器

大多数支持选项文件的MySQL程序都处理以下选项。因为这些选项会影响选项文件的处理&#xff0c;所以必须在命令行上给出&#xff0c;而不是在选项文件中给出。为了正常工作&#xff0c;这些选项中的每一个都必须先于其他选项给出&#xff0c;但以下情况除外&#xff1a; -prin…

OpenCASCADE开发指南<十四>:OCCT建模类之BRepPrimAPI_MakePipe创建管道

1、OpenCasCade拓扑几何 在Open CASCADE Technology (OCCT) 中,除了基本三维几何体建模类BRepBuilderAPI外,还提供了复杂模型的建模类,常用的有如下几种,他们可以单独使用或相互组合,通过OCCT提供的融合函数进行组装。例如:BRepOffsetAPI_ThruSections、BRepOffsetAPI_Ma…

sqlite基本操作

简介 文章目录 简介1.数据库的安装2.数据库命令&#xff1a;API&#xff0c;创建表单代码 csprintf&#xff08;&#xff09;getchar和scanf&#xff08;&#xff09; 1.数据库的安装 sudo dpkg -i *.deb这个报错表明出现依赖问题 用这个命令后再试试sudo apt --fix-broken in…

Docker是什么?使用场景作用及Docker的安装和启动详解

目录 Docker是什么&#xff1f; Docker的发展 Docker的安装 Docker使用 Docker的运行机制 第一个Docker容器 进入Docker容器 客户机访问容器 Docker是什么&#xff1f; Docker 是一个开源的应用容器引擎&#xff0c;基于 Go 语言 并遵从 Apache2.0 协议开源。 Docker …

ChatGPT的基本原理是什么?又该如何提高其准确性?

在深入探索如何提升ChatGPT的准确性之前&#xff0c;让我们先来了解一下它的工作原理吧。ChatGPT是一种基于深度学习的自然语言生成模型&#xff0c;它通过预训练和微调两个关键步骤来学习和理解自然语言。 在预训练阶段&#xff0c;ChatGPT会接触到大规模的文本数据集&#x…

绘画参数配置及使用

绘画参数配置及使用 路径&#xff1a;站点后台-功能-AI绘画 进入参数配置 接口选择&#xff1a;多种接口自主选择&#xff08;需自己准备key&#xff09;&#xff0c;对应接口的key对话和绘画通用 存储空间&#xff1a; 位置在超管后台-存储空间 自主选择存储&#xff08;需…

冯喜运:6.3周一黄金原油行情分析及操作建议

【黄金消息面分析】&#xff1a;上周行情概述&#xff1a;现货黄金上周&#xff08;0527-0531&#xff09;反弹上探&#xff0c;5月27号开盘前本人曾提醒关注反弹&#xff0c;较当时上涨约30美元&#xff0c;最高至2364一线&#xff0c;其后震荡下跌。周线小幅收跌0.27%&#x…

微服务:Rabbitmq的WorkQueue模型的使用、默认消费方式(消息队列中间件)

文章目录 WorkQueue模型控制预取消息个数 WorkQueue模型 当然&#xff0c;一个队列&#xff0c;可以由多个消费者去监听。 来实现一下. 生产者&#xff1a; Testpublic void testWorkQueue() throws InterruptedException {// 队列名称String queueName "simple.queue…

通过提示工程将化学知识整合到大型语言模型中

在当今快速发展的人工智能领域&#xff0c;大型语言模型&#xff08;LLMs&#xff09;正成为科学研究的新兴工具。这些模型以其卓越的语言处理能力和零样本推理而闻名&#xff0c;为解决传统科学问题提供了全新的途径。然而&#xff0c;LLMs在特定科学领域的应用面临挑战&#…