进阶 -2- 光纤通信
约 2905 个字 545 行代码 2 张图片 预计阅读时间 16 分钟
章节导读
在现代高速通信系统中,光纤通信作为一种重要的信息传输手段,广泛应用于数据中心、城域网、广域网以及各种嵌入式高速数据传输场景中。相比传统电缆传输,光纤具有带宽高、传输距离远、抗电磁干扰能力强、保密性好等显著优势,是实现高速、高可靠性通信的关键技术。
随着 FPGA 在通信领域的深入应用,基于 FPGA 的光纤通信系统设计成为一项非常实用的技能。在本实验中,我们将基于开发板配套的光纤接口模块,设计并实现一个基础的光纤通信收发系统。通过本实验,同学们将掌握光纤收发器(SFP 模块)的基本使用方法,了解高速串行通信,8b10b 编码等概念,并学会如何通过 FPGA 完成数据的高速发送与接收。
理论学习
8B10B 编码
8b/10b 编码常用于光纤通信和 LVDS 信号中,其主要目的是解决信号传输过程中的直流不平衡问题。由于串行电路通常采用交流耦合的方式(串接电容)我们知道理想电容的阻抗公式
\[
Zc=\frac{1}{2pif*C}
\]
通过这个公式可以知道,频率 f 越高,阻抗越低,反之,频率越低,阻抗越高。

Figure 1. 交流耦合
如上图所示,当码型是高频的时候,基本可以不损耗的传输过去,但是当码型为连续的 0 或者 1 的时候,电容的损耗就很大,导致幅度不断降低,最严重的后果就是无法识别到底是 0 还是 1,因此 8b/10b 编码就是为了尽量把低频的码型优化成较高频率的码型,从而降低阻抗带来的损耗。以光模块为例,它只能传输“亮”或“不亮”两种状态,即二进制的 1 和 0。当传输数据中出现长时间连续的 1 或 0(如 111111100000000) 时,线路中电容的损耗就会很大,从而无法准确采样,进而造成误判。
同样,在 LVDS 高速传输中也存在类似问题。如果传输的 0 和 1 数量长期不均衡,线路上的基准电压会发生偏移,影响信号的正确识别。
因此,8b/10b 编码通过对每 8 位数据编码成 10 位信号,实现传输数据的直流平衡和足够的码流转换,也就是保证串行数据不会连续出现 1 和 0,从而提升系统的可靠性和抗干扰能力。
那么 8b10b 编码是如何将 8bit 数据编码成 10bit 数据的呢?假设原始 8 位数据从高到低用 HGFEDCBA 表示,将 8 位数据分成高 3 位 HGF 和低 5 位 EDCBA 两个子组。经过 5B/6B 编码,将低 5 位 EDCBA 映射成 abcdei;高 3 位经过 3B/4B 编码,映射成 fghj,最后合成 abcdeifghj 发送。

Figure 2. 8b10b 编码原理
可见 8b10b 编码是基于 5b/6b 和 3b/4b 之上的。
| input |
HGF |
RD = -1 |
RD = +1 |
input |
HGF |
RD = -1 |
RD = +1 |
| D.x.0 |
000 |
1011 |
0100 |
K.x.0 |
000 |
1011 |
0100 |
| D.x.1 |
001 |
1001 |
1001 |
K.x.1 ‡ |
001 |
0110 |
1001 |
| D.x.2 |
010 |
0101 |
0101 |
K.x.2 ‡ |
010 |
1010 |
0101 |
| D.x.3 |
011 |
1100 |
0011 |
K.x.3 |
011 |
1100 |
0011 |
| D.x.4 |
100 |
1101 |
0010 |
K.x.4 |
100 |
1101 |
0010 |
| D.x.5 |
101 |
1010 |
1010 |
K.x.5 ‡ |
101 |
0101 |
1010 |
| D.x.6 |
110 |
0110 |
0110 |
K.x.6 ‡ |
110 |
1001 |
0110 |
| D.x.P7 † |
111 |
1110 |
0001 |
K.x.7 ‡ |
111 |
0111 |
1000 |
| D.x.A7 † |
111 |
0111 |
1000 |
|
|
|
|
Table 1. 5b/6b 和 3b/4b 编码表
| input |
HGF EDCBA |
RD = -1 (abcdei fghj) |
RD = +1 (abcdei fghj) |
| K.28.0 |
000 11100 |
001111 0100 |
110000 1011 |
| K.28.1 † |
001 11100 |
001111 1001 |
110000 0110 |
| K.28.2 |
010 11100 |
001111 0101 |
110000 1010 |
| K.28.3 |
011 11100 |
001111 0011 |
110000 1100 |
| K.28.4 |
100 11100 |
001111 0010 |
110000 1101 |
| K.28.5 † |
101 11100 |
001111 1010 |
110000 0101 |
| K.28.6 |
110 11100 |
001111 0110 |
110000 1001 |
| K.28.7 ‡ |
111 11100 |
001111 1000 |
110000 0111 |
| K.23.7 |
111 10111 |
111010 1000 |
000101 0111 |
| K.27.7 |
111 11011 |
110110 1000 |
001001 0111 |
| K.29.7 |
111 11101 |
101110 1000 |
010001 0111 |
| K.30.7 |
111 11110 |
011110 1000 |
100001 0111 |
Table 2. 8b10b 特殊字符表 (1)
上述两张表格就是 5b/6b 和 3b/4b 编码表,那我们先来看懂这两张表格。
首先是 D.x.y,将低 5 位 EDCBA 按其十进制数值记为 x,将高 3 位按其十进制数值记为 y,将原始 8bit 数据记为 D.x.y。例如 8bit 数据最大为 255,也就是 111_11111,低五位是 11111,也就是十进制 31,高三位 111,是十进制 7,所对应的 8b10b 就是 D.31.7。这也说明了上述 5b/6b 编码最大为 D.31,3b/4b 编码最大为 D.x.7。
至于 RD = - 1 和 RD = + 1。+ - 分别表示 0 比 1 多,1 比 0 多,多的个数是一样的,而且最多多两个。当 0 和 1 一样多时便没有 +1-1 之分。编码的初始化状态是 -1。
对于 D.x.7† ,当和 5B/6B 组合时 D.x.P7 和 D.x.A7 编码必须选择一个来避免连续的 5 个 0 或 1。D.x.A7 用在:x=17 x=18 x=20 当 RD=-1 时; x=11 x=13 x=14 当 RD=+1 时。 其他情况下 x.A7 码不能被使用。
表里面 D.23,D.27,D.29,D.30 后面带 †,它有自己的特殊配对规则,当 x=23 x=27 x=29 x=30 时,3b/4b 这边使用 K.x.7 进行编码。
除了上面的一些编码,8b10b 编码还规定了一些特殊的字符,也称为 K 码。K 码表如下所示:
| input |
HGF EDCBA |
RD = -1 (abcdei fghj) |
RD = +1 (abcdei fghj) |
| K.28.0 |
000 11100 |
001111 0100 |
110000 1011 |
| K.28.1 † |
001 11100 |
001111 1001 |
110000 0110 |
| K.28.2 |
010 11100 |
001111 0101 |
110000 1010 |
| K.28.3 |
011 11100 |
001111 0011 |
110000 1100 |
| K.28.4 |
100 11100 |
001111 0010 |
110000 1101 |
| K.28.5 † |
101 11100 |
001111 1010 |
110000 0101 |
| K.28.6 |
110 11100 |
001111 0110 |
110000 1001 |
| K.28.7 ‡ |
111 11100 |
001111 1000 |
110000 0111 |
| K.23.7 |
111 10111 |
111010 1000 |
000101 0111 |
| K.27.7 |
111 11011 |
110110 1000 |
001001 0111 |
| K.29.7 |
111 11101 |
101110 1000 |
010001 0111 |
| K.30.7 |
111 11110 |
011110 1000 |
100001 0111 |
Table 3. 8b10b 特殊字符表 (2)
8B/10B 标准中使用了 12 个特殊的控制代码,他们能在数据中被发送,利用这些控制字符,可以组成各种控制语句,其中 K.28.1 K.28.5 K.28.7 被称为逗号字符,利用序列检测状态机检测逗号字符,以保证在串行数据接收过程中接收到数据的唯一性。如果没有逗号字符,串行接收设备将不知道数据是从哪一位开始,当序列检测器检测到逗号字符时就确定了字符的开始。
hsstlp ip 核
实验板小眼睛盘古 pg2l100h 有 2 个 hsst 硬核,共 8 路全双工高速串行收发接口,每个接口最高支持 6.6Gbps 的速度,其中 4 路用于 PCIE,2 路用于 SMA 接口,2 路用于 sfp 光纤接口。有关 hsstlp 的内容可以参考官方手册。
图3.HSSP ip核界面
实战演练
实验目标
利用板载一路 sfp 光纤通道接收来自 ctrlFPGA 的数据实现光纤通信。
硬件资源
实验板上有 2 路 sfp 接口全部与 ctrl-FPGA 相连。
图4.板载sfp接口
程序设计
首先我们应该学会配置并使用 hsst ip 核。
在 ip 核的配置界面可以看到可以配置 4 个通道的状态,分别是全双工,仅接收,仅发送和不使能。同时还可以给每路通道选择不同的标准协议例如 GE,XAUI 等,勾选 Protocol Default Setting 后可以使用该协议的默认配置。也可以自定义协议传输。
在本次实验中,我们将通道 3 设置成全双工,自定义协议,线速率 5Gbps,编码选择 8b10b,传输和接收的数据长度选择 32 位,PLL 的参考时钟为 125M。配置图如下:
图5.hsst ip核
在 Alignment 界面,可以配置对齐(word alignment)的模式,对齐所用的 K 码,多通道绑定(channel bonding)的模式,以及高速传输时出现的时钟偏差补偿(Clock Tolerance Compensation),具体使用可以参考 hsst 手册。
本次实验配置字节对齐模式为用户自定义,K 码选择 K28.5,不使用通道对齐和时钟偏差补偿。配置如下图所示:
图6.hsst ip核
在 Misc 界面选择使用官方复位程序,参考时钟 27M(板载时钟),复位时钟个数为 32767,接收模式(RX Termination Mode)选择外部 AC,内部 DC。配置如下图所示:
图7.hsst ip核
生成 ip 核后,我们将 ip 核例化在顶层文件中,目前配置的 ip 核包括复位所需要的信号,重要的状态信号,传输时钟 i_p_tx3_clk_fr_core,传输的 32 位数据 i_txd_3,传输的数据类型 i_txk_3,接收时钟 i_p_rx3_clk_fr_core,接收的 32 位数据 o_rxd_3,接收的数据类型 o_rxk_3。这里解释一下 txk 和 rxk,这个信号是 4 位的,每一位对应 txd 和 rxd 信号的 8 位数据,1 表示该 8 位数据是 k 码,0 表示改 8 位数据是普通数据。参考代码如下:
| Verilog |
|---|
| module hsst_top (
input clk,
input rstn,
input i_p_refckn_0,
input i_p_refckp_0,
output [7:0] led
);
wire i_free_clk;
wire i_pll_rst_0;
wire o_pll_done_0;
wire o_txlane_done_3;
wire o_rxlane_done_3;
wire o_p_clk2core_tx_3;
wire i_p_tx3_clk_fr_core;
wire o_p_clk2core_rx_3;
wire i_p_rx3_clk_fr_core;
wire o_p_pll_lock_0;
wire o_p_rx_sigdet_sta_3;
wire o_p_lx_cdr_align_3;
wire i_p_l3rxn;
wire i_p_l3rxp;
wire o_p_l3txn;
wire o_p_l3txp;
wire [3:0] i_tdispsel_3;
wire [3:0] i_tdispctrl_3;
wire [2:0] o_rxstatus_3;
wire [3:0] o_rdisper_3;
wire [3:0] o_rdecer_3;
wire txclk;
reg [31:0] i_txd_3;
reg [ 3:0] i_txk_3;
wire rxclk;
wire [31:0] o_rxd_3;
wire [ 3:0] o_rxk_3;
assign i_free_clk = clk;
assign i_pll_rst_0 = rstn;
assign i_p_tx3_clk_fr_core = o_p_clk2core_tx_3;
assign i_p_rx3_clk_fr_core = o_p_clk2core_rx_3;
assign txclk = i_p_tx3_clk_fr_core;
assign rxclk = i_p_rx3_clk_fr_core;
hsst the_instance_name (
.i_free_clk (i_free_clk ), //复位序列参考时钟
.i_pll_rst_0 (i_pll_rst_0 ), //pll复位
.i_wtchdg_clr_0 (), //
.o_wtchdg_st_0 (), //
.i_p_refckn_0 (i_p_refckn_0 ), //pll参考差分时钟
.i_p_refckp_0 (i_p_refckp_0 ), //
.o_pll_done_0 (o_pll_done_0 ), //pll复位完成
.o_txlane_done_3 (o_txlane_done_3 ), //tx通道初始化完成
.o_rxlane_done_3 (o_rxlane_done_3 ), //rx通道初始化完成
.o_p_pll_lock_0 (o_p_pll_lock_0 ), //pll lock信号
.o_p_rx_sigdet_sta_3 (o_p_rx_sigdet_sta_3), //sigdet_sta状态信号
.o_p_lx_cdr_align_3 (o_p_lx_cdr_align_3 ), //cdr_align状态信号
.o_p_clk2core_tx_3 (o_p_clk2core_tx_3 ), //output传输时钟
.i_p_tx3_clk_fr_core (i_p_tx3_clk_fr_core), //input
.o_p_clk2core_rx_3 (o_p_clk2core_rx_3 ), //output接收时钟
.i_p_rx3_clk_fr_core (i_p_rx3_clk_fr_core), //input
.i_p_pcs_word_align_en_3 (1'b1 ), //使能word_align
.i_p_l3rxn (i_p_l3rxn ), //差分数据线
.i_p_l3rxp (i_p_l3rxp ), //差分数据线
.o_p_l3txn (o_p_l3txn ), //差分数据线
.o_p_l3txp (o_p_l3txp ), //差分数据线
.i_txd_3 (i_txd_3 ), //传输的32位数据
.i_tdispsel_3 (i_tdispsel_3 ), //
.i_tdispctrl_3 (i_tdispctrl_3 ), //
.i_txk_3 (i_txk_3 ), //传输的数据类型,0:普通数据,1:K码
.o_rxstatus_3 (o_rxstatus_3 ), //
.o_rxd_3 (o_rxd_3 ), //接收的32位数据
.o_rdisper_3 (o_rdisper_3 ), //
.o_rdecer_3 (o_rdecer_3 ), //
.o_rxk_3 (o_rxk_3 ) //接收的数据类型,0:普通数据,1:K码
);
//*******************************************************************//
assign led = {1'b0,1'b0,o_pll_done_0,o_p_pll_lock_0,o_txlane_done_3,o_p_rx_sigdet_sta_3, o_p_lx_cdr_align_3,o_rxlane_done_3};
reg [ 2:0] tx_state;
reg [15:0] txcnt;
wire tx_rstn = rstn && o_txlane_done_3 && o_rxlane_done_3;
always @(posedge txclk or negedge tx_rstn)begin
if(~tx_rstn)begin
i_txd_3 <= 32'hBCBCBCBC;
i_txk_3 <= 4'b1111;
end
else if(tx_state == 0)begin
i_txd_3 <= 32'hBCBCBCBC;
i_txk_3 <= 4'b1111;
end
else if(tx_state == 1)begin
i_txd_3 <= 32'h00000000;
i_txk_3 <= 4'b0000;
end
else if(tx_state == 2)begin
i_txd_3 <= i_txd_3 + 1;
i_txk_3 <= 4'b0000;
end
end
always @(posedge txclk or negedge tx_rstn)begin
if(~tx_rstn)begin
tx_state <= 0;
end
else if(tx_state == 0)begin
if(txcnt == 7)
tx_state <= 1;
else
tx_state <= 0;
end
else if(tx_state == 1)begin
tx_state <= 2;
end
else if(tx_state == 2)begin
if(txcnt == 1032)
tx_state <= 0;
else
tx_state <= 2;
end
end
always @(posedge txclk or negedge tx_rstn)begin
if(~tx_rstn)
txcnt <= 0;
else if(txcnt == 1032)
txcnt <= 0;
else
txcnt <= txcnt + 1;
end
endmodule
|
到现在为止,我们已经可以接收和发送数据了,但使用的时候会发现一个问题,如下图所示,我们发送的数据是 0xc5bcc5bc, 但接收到的数据却是 0xbcc5bc5,数据出现了移位,这是为什么呢?这是因为发送编码是 8b10b,是以 8 位数据为一个基本单位进行发送的,虽然发送数据是 0xc5bcc5bc,但是在串行通信中,他并不知道你发送的 32 位数据是以 c5 开头还是以 bc 开头,前面在 ip 核中设置的 word alignment 正确的对齐了 BC(k28.5)这个字符,正确的接收了字节数据,但 32 位数据中字节顺序需要我们自己去调整。(因为设置 ip 时用的自定义模式)。
图8.debugger抓取波形
为了解决这一问题,我们也要给他设计一个模块,使他自动实现接收数据与发送数据一致,不会出现错位。在本实验中空闲时发送逗号字符 K28.5 (BC),有数据要发送时发送 32 位数据。根据此格式设计对齐模块参考代码如下:
| Verilog |
|---|
| module Word_Alignment_32bit (
input wire clk ,
input wire rstn ,
input wire [31:0] data_bf_align /* synthesis PAP_MARK_DEBUG="1" */,
input wire [ 3:0] rxk /* synthesis PAP_MARK_DEBUG="1" */,
output reg data_valid /* synthesis PAP_MARK_DEBUG="1" */,
output reg [31:0] data_af_align /* synthesis PAP_MARK_DEBUG="1" */,
output reg data_done/* synthesis PAP_MARK_DEBUG="1" */
);
//************************ 8b10b K_Code ********************************************
//K28.0 1C
//K28.1 3C
//K28.2 5C
//K28.3 7C
//K28.4 9C
//K28.5 BC
//K28.6 DC
//K28.7 FC
//K23.7 F7
//K27.7 FB
//K29.7 FD
//K30.7 FE
//********************** Pattern Controller ********************************************
// txdata format
// data_x = {data_x_1,data_x_2,data_x_3,data_x_4}
// 假如发送数据是data_1,data_2,data_3
// 接收数据很可能会出现
// {data_1}
//
//
//
//
//
//
// data Format:
//
// __ ________ ________ ________ ________ ________ ________ ________ ________ ________
// __X_ idle__X__idle__X__idle__X__data__x ……………… x__data__X__idle__X__idle__X__idle__X___idle_
//
// idle <= K28.5
// 空闲时发送K28.5,有数据时发送数据,
// 本模块根据以上格式实现32位数据对齐,使得接收数据与发送数据一致,不会出现错位。
//
//
//**************************************************************************************
//parameter
localparam IDLE = 0;
localparam ALIGN1 = 1;
localparam ALIGN2 = 2;
localparam ALIGN3 = 3;
localparam ALIGN4 = 4;
reg [4:0] state;
reg [4:0] nextstate;
reg skip;
reg rxcnt;
reg [ 7:0] datareg8;
reg [15:0] datareg16;
reg [23:0] datareg24;
reg [31:0] datareg32;
reg error;
// always @(negedge clk or negedge rstn)begin
// if(~rstn)begin
// datareg8 <= 0;
// datareg16 <= 0;
// datareg24 <= 0;
// datareg32 <= 0;
// end
// else begin
// datareg8 <= data_bf_align[31:24];
// datareg16 <= data_bf_align[31:16];
// datareg24 <= data_bf_align[31:8];
// datareg32 <= data_bf_align;
// end
// end
always @(posedge clk or negedge rstn)begin
if(~rstn)begin
state <= IDLE;
end
else begin
state <= nextstate;
end
end
always @(*)begin
case(state)
IDLE : begin
if (rxk == 4'b0111) nextstate <= ALIGN1;
else if(rxk == 4'b0011) nextstate <= ALIGN2;
else if(rxk == 4'b0001) nextstate <= ALIGN3;
else if(rxk == 4'b0000) nextstate <= ALIGN4;
else nextstate <= IDLE;
end
ALIGN1 : begin
if(skip || error) begin
if (rxk == 4'b0111) nextstate <= ALIGN1;
else if(rxk == 4'b0011) nextstate <= ALIGN2;
else if(rxk == 4'b0001) nextstate <= ALIGN3;
else if(rxk == 4'b0000) nextstate <= ALIGN4;
else nextstate <= IDLE;
end
else
nextstate <= ALIGN1;
end
ALIGN2 : begin
if(skip || error) begin
if (rxk == 4'b0111) nextstate <= ALIGN1;
else if(rxk == 4'b0011) nextstate <= ALIGN2;
else if(rxk == 4'b0001) nextstate <= ALIGN3;
else if(rxk == 4'b0000) nextstate <= ALIGN4;
else nextstate <= IDLE;
end
else
nextstate <= ALIGN2;
end
ALIGN3 : begin
if(skip || error) begin
if (rxk == 4'b0111) nextstate <= ALIGN1;
else if(rxk == 4'b0011) nextstate <= ALIGN2;
else if(rxk == 4'b0001) nextstate <= ALIGN3;
else if(rxk == 4'b0000) nextstate <= ALIGN4;
else nextstate <= IDLE;
end
else
nextstate <= ALIGN3;
end
ALIGN4 : begin
if(skip || error) begin
if (rxk == 4'b0111) nextstate <= ALIGN1;
else if(rxk == 4'b0011) nextstate <= ALIGN2;
else if(rxk == 4'b0001) nextstate <= ALIGN3;
else if(rxk == 4'b0000) nextstate <= ALIGN4;
else nextstate <= IDLE;
end
// if(skip)
// nextstate <= IDLE;
// else if(error)
// nextstate <= IDLE;
else
nextstate <= ALIGN4;
end
endcase
end
always @(posedge clk or negedge rstn) begin
if(~rstn)begin
data_valid <= 0;
data_af_align <= 0;
data_done <= 0;
rxcnt <= 0;
skip <= 0;
datareg8 <= 0;
datareg16 <= 0;
datareg24 <= 0;
datareg32 <= 0;
error <= 0;
end
else begin
// data_valid <= 0;
// data_done <= 0;
case(state)
IDLE : begin
rxcnt <= 0;
skip <= 0;
data_valid <= 0;
data_done <= 0;
if(rxk == 4'b1111)begin
data_af_align <= data_bf_align;
error <= 0;
end
else begin
if(rxk == 4'b0111)begin
datareg8 <= data_bf_align[31:24];
error <= 0;
end
else if(rxk == 4'b0011)begin
datareg16 <= data_bf_align[31:16];
error <= 0;
end
else if(rxk == 4'b0001)begin
datareg24 <= data_bf_align[31: 8];
error <= 0;
end
else if(rxk == 4'b0000)begin
datareg32 <= data_bf_align;
error <= 0;
end
else begin
error <= 1;
end
end
end
ALIGN1 : begin
data_af_align <= {data_bf_align[23:0],datareg8};
datareg8 <= data_bf_align[31:24];
if(skip) skip <= 0;
else if(rxk == 4'b1000) skip <= 1;
else if(rxk == 4'b0000) skip <= 0;
else error <= 1;
if(skip) data_done <= 0;
else if(rxk == 4'b1000) data_done <= 1;
else data_done <= 0;
if(skip) data_valid <= 0;
else data_valid <= 1;
end
ALIGN2 : begin
data_af_align <= {data_bf_align[15:0],datareg16};
datareg16 <= data_bf_align[31:16];
if(skip) skip <= 0;
else if(rxk == 4'b1100) skip <= 1;
else if(rxk == 4'b0000) skip <= 0;
else error <= 1;
if(skip) data_done <= 0;
else if(rxk == 4'b1100) data_done <= 1;
else data_done <= 0;
if(skip) data_valid <= 0;
else data_valid <= 1;
end
ALIGN3 : begin
data_af_align <= {data_bf_align[7:0],datareg24};
datareg24 <= data_bf_align[31:8];
if(skip) skip <= 0;
else if(rxk == 4'b1110) skip <= 1;
else if(rxk == 4'b0000) skip <= 0;
else error <= 1;
if(skip) data_done <= 0;
else if(rxk == 4'b1110) data_done <= 1;
else data_done <= 0;
if(skip) data_valid <= 0;
else data_valid <= 1;
end
ALIGN4 : begin
data_af_align[31:0] <= datareg32;
datareg32 <= data_bf_align;
if(skip) skip <= 0;
else if(rxk == 4'b1111) skip <= 1;
else if(rxk == 4'b0000) skip <= 0;
else error <= 1;
if(skip) data_done <= 0;
else if(rxk == 4'b1111) data_done <= 1;
else data_done <= 0;
if(skip) data_valid <= 0;
else data_valid <= 1;
end
endcase
end
end
endmodule
|
我们将对齐模块例化到顶层,并且为了观察接收数据与发送数据是否一致,我们将接收到的数据从 0 开始比较,如果相同,led0 就会亮,如果不同 led0 就不亮,同时把未经 32 位对齐模块的数据与发送数据对比,相同 led1 亮,不同 led1 不亮,新的顶层模块如下所示:
| Verilog |
|---|
| module hsst_top (
input clk,
input rstn,
input i_p_refckn_0,
input i_p_refckp_0,
output [7:0] led
);
wire i_free_clk;
wire i_pll_rst_0;
wire o_pll_done_0;
wire o_txlane_done_3;
wire o_rxlane_done_3;
wire o_p_clk2core_tx_3;
wire i_p_tx3_clk_fr_core;
wire o_p_clk2core_rx_3;
wire i_p_rx3_clk_fr_core;
wire o_p_pll_lock_0;
wire o_p_rx_sigdet_sta_3;
wire o_p_lx_cdr_align_3;
wire i_p_l3rxn;
wire i_p_l3rxp;
wire o_p_l3txn;
wire o_p_l3txp;
wire [3:0] i_tdispsel_3;
wire [3:0] i_tdispctrl_3;
wire [2:0] o_rxstatus_3;
wire [3:0] o_rdisper_3;
wire [3:0] o_rdecer_3;
wire txclk;
reg [31:0] i_txd_3;
reg [ 3:0] i_txk_3;
wire rxclk;
wire [31:0] o_rxd_3;
wire [ 3:0] o_rxk_3;
assign i_free_clk = clk;
assign i_pll_rst_0 = rstn;
assign i_p_tx3_clk_fr_core = o_p_clk2core_tx_3;
assign i_p_rx3_clk_fr_core = o_p_clk2core_rx_3;
assign txclk = i_p_tx3_clk_fr_core;
assign rxclk = i_p_rx3_clk_fr_core;
hsst the_instance_name (
.i_free_clk (i_free_clk ), //复位序列参考时钟
.i_pll_rst_0 (i_pll_rst_0 ), //pll复位
.i_wtchdg_clr_0 (), //
.o_wtchdg_st_0 (), //
.i_p_refckn_0 (i_p_refckn_0 ), //pll参考差分时钟
.i_p_refckp_0 (i_p_refckp_0 ), //
.o_pll_done_0 (o_pll_done_0 ), //pll复位完成
.o_txlane_done_3 (o_txlane_done_3 ), //tx通道初始化完成
.o_rxlane_done_3 (o_rxlane_done_3 ), //rx通道初始化完成
.o_p_pll_lock_0 (o_p_pll_lock_0 ), //pll lock信号
.o_p_rx_sigdet_sta_3 (o_p_rx_sigdet_sta_3), //sigdet_sta状态信号
.o_p_lx_cdr_align_3 (o_p_lx_cdr_align_3 ), //cdr_align状态信号
.o_p_clk2core_tx_3 (o_p_clk2core_tx_3 ), //output传输时钟
.i_p_tx3_clk_fr_core (i_p_tx3_clk_fr_core), //input
.o_p_clk2core_rx_3 (o_p_clk2core_rx_3 ), //output接收时钟
.i_p_rx3_clk_fr_core (i_p_rx3_clk_fr_core), //input
.i_p_pcs_word_align_en_3 (1'b1 ), //使能word_align
.i_p_l3rxn (i_p_l3rxn ), //差分数据线
.i_p_l3rxp (i_p_l3rxp ), //差分数据线
.o_p_l3txn (o_p_l3txn ), //差分数据线
.o_p_l3txp (o_p_l3txp ), //差分数据线
.i_txd_3 (i_txd_3 ), //传输的32位数据
.i_tdispsel_3 (i_tdispsel_3 ), //
.i_tdispctrl_3 (i_tdispctrl_3 ), //
.i_txk_3 (i_txk_3 ), //传输的数据类型,0:普通数据,1:K码
.o_rxstatus_3 (o_rxstatus_3 ), //
.o_rxd_3 (o_rxd_3 ), //接收的32位数据
.o_rdisper_3 (o_rdisper_3 ), //
.o_rdecer_3 (o_rdecer_3 ), //
.o_rxk_3 (o_rxk_3 ) //接收的数据类型,0:普通数据,1:K码
);
//*******************************************************************//
reg [ 2:0] tx_state;
reg [15:0] txcnt;
wire tx_rstn = rstn && o_txlane_done_3 && o_rxlane_done_3;
always @(posedge txclk or negedge tx_rstn)begin
if(~tx_rstn)begin
i_txd_3 <= 32'hBCBCBCBC;
i_txk_3 <= 4'b1111;
end
else if(tx_state == 0)begin
i_txd_3 <= 32'hBCBCBCBC;
i_txk_3 <= 4'b1111;
end
else if(tx_state == 1)begin
i_txd_3 <= 32'h00000000;
i_txk_3 <= 4'b0000;
end
else if(tx_state == 2)begin
i_txd_3 <= i_txd_3 + 1;
i_txk_3 <= 4'b0000;
end
end
always @(posedge txclk or negedge tx_rstn)begin
if(~tx_rstn)begin
tx_state <= 0;
end
else if(tx_state == 0)begin
if(txcnt == 7)
tx_state <= 1;
else
tx_state <= 0;
end
else if(tx_state == 1)begin
tx_state <= 2;
end
else if(tx_state == 2)begin
if(txcnt == 1032)
tx_state <= 0;
else
tx_state <= 2;
end
end
always @(posedge txclk or negedge tx_rstn)begin
if(~tx_rstn)
txcnt <= 0;
else if(txcnt == 1032)
txcnt <= 0;
else
txcnt <= txcnt + 1;
end
wire Word_Alignment_rstn = rstn && o_txlane_done_3 && o_rxlane_done_3;
wire data_valid;
wire [31:0] data_af_align;
wire data_last;
Word_Alignment_32bit Word_Alignment_32bit_inst (
.clk (rxclk ),
.rstn (Word_Alignment_rstn),
.data_bf_align (o_rxd_3 ),
.rxk (o_rxk_3 ),
.data_valid (data_valid ),
.data_af_align (data_af_align ),
.data_done (data_last )
);
//**********************************//
reg [31:0] data_bf_Alignment_judge;
reg [31:0] data_af_Alignment_judge;
always@(posedge rxclk or negedge Word_Alignment_rstn)begin
if(~Word_Alignment_rstn)data_bf_Alignment_judge <= 0;
else if(o_rxk_3 == 4'b0000) data_bf_Alignment_judge <= data_bf_Alignment_judge + 1;
else data_bf_Alignment_judge <= 0;
end
always@(posedge rxclk or negedge Word_Alignment_rstn)begin
if(~Word_Alignment_rstn)data_af_Alignment_judge <= 0;
else if(data_valid) data_af_Alignment_judge <= data_af_Alignment_judge + 1;
else data_af_Alignment_judge <= 0;
end
assign led = {o_pll_done_0,o_p_pll_lock_0,o_txlane_done_3,o_rxlane_done_3,1'b0,1'b0,data_af_Alignment_judge == data_af_align,data_bf_Alignment_judge == o_rxd_3};
endmodule
|
仿真验证
本实验不进行仿真,可以点击工程目录下 ipcore 文件夹下,hsst 参考设计中 sim 文件夹的 sim.bat 文件进行仿真。
上板验证
上板前要先进行管脚约束。端口除了板载时钟,复位以及 led,还需要添加以下约束:
| Text Only |
|---|
| define_attribute {p:i_p_refckn_0} {PAP_IO_DIRECTION} {INPUT}
define_attribute {p:i_p_refckn_0} {PAP_IO_LOC} {AB11}
define_attribute {p:i_p_refckn_0} {PAP_IO_UNUSED} {TRUE}
define_attribute {p:i_p_refckp_0} {PAP_IO_DIRECTION} {INPUT}
define_attribute {p:i_p_refckp_0} {PAP_IO_LOC} {AA11}
define_attribute {p:i_p_refckp_0} {PAP_IO_UNUSED} {TRUE}
define_attribute {i:hsst_inst.U_GTP_HSSTLP_WRAPPER.CHANNEL3_ENABLE.U_GTP_HSSTLP_LANE3} {PAP_LOC} {HSSTLP_364_0:U3_HSSTLP_LANE}
define_attribute {i:hsst_inst.U_GTP_HSSTLP_WRAPPER.PLL0_ENABLE.U_GTP_HSSTLP_PLL0} {PAP_LOC} {HSSTLP_364_0:U0_HSSTLP_PLL}
|
该语句主要约束 hsst 参考时钟管脚,hsst 所使用 PLL(PLL0),以及 hsst 使用的通道。其他约束可以参考工程目录下 ipcore 文件夹中 hsst 内的参考例程。
完成管脚分配之后就可以生成 sbit 文件,将文件提交到网站后点击烧录,即可将 sbit 下载到实验板中,在摄像头页面即可观察到 8 个 led 中前四个亮,后四个分别为不亮,不亮,亮,不亮的状态。前四个亮表示 hsst 中 pll 和 rx,tx 通道初始化成功,最后两个 led,led1 亮表示接收的与发送的数据相同,led0 不亮表示未经 32 位对齐模块处理的接收数据与发送数据不同。
章末总结
本次实验主要学习了高速串行通信的相关知识,初步学会使用 hsst ip 核。