在之前的章节中,我们学习了高速通信的发送,接收等模块的设计, 同时展示了仿真程序的使用。这节课我们在FII-PRA006 Altera FPGA 开发板上做一个综合实验,验证高速通信的数据发送和接收。
之前的高速通信工程设计:

图1
在之前的工程设计中, 我们使用仿真程序来发送数据,检查算法的实现。 本节课程将对工程进行改造。
实验目的:
- 用FII-PRA006 FPGA开发板验证高速串行通信工程
- 学习IP核开发流程:分析需求,设计IP核,仿真验证,到真实的下板验证,最后封装
- 用示波器观察实际电路上的波形
- 使用SignalTap Logic Analyzer工具来抓取通信中的数据包
1.设计顶层模块
顶层模块是由前面工程的仿真文件tb_8b10b.v修改生成的,新添加了板载的时钟输入inclk,系统pll_rst 信号, 以及 tx,rx 的LVDS信号。使用宏在仿真和下板验证之间切换,详情见下列代码。

图2
重新设计的顶层模块,top.v 将包括hs_txrx.v 文件。
代码如下:
`timescale 1 ns / 1 ps
`define SIM
module top
#(
parameter IDLE = 10'b1010_1010_10
)
`ifdef SIM
();
parameter PERIOD = 20 ; //周期20
reg inclk ; // 模拟板级提供的输入时钟
initial begin
inclk = 1'b0;
#(PERIOD/2);
forever
#(PERIOD/2) inclk = ~inclk;
end
reg pll_rst; //实体模块PLL的rst信号
wire tx_rx; // 收发在testbench的回环信号
initial begin
pll_rst=1'b1;
#50 pll_rst=1'b0;
#50000;
$stop;
end
`else
(
input inclk,
output tx,
input rx,
input pll_rst
);
`endif
wire pll_locked;
wire reset = !pll_locked;
wire tx_rdy;
reg [ 7: 0 ] tx_data;
reg tx_en;
reg tx_vld;
wire tx_vld_ack;
wire rx_data_vld;
wire rx_data_rdy;
wire ko;
wire [ 7: 0 ] rx_data;
wire clk_100M;
localparam LEN = 16;
reg [ 7: 0 ] frame_len;
reg [ 2: 0 ] tx_st;
always@( posedge clk_100M or posedge reset )
if ( reset )
begin
tx_vld <= 1'b0;
tx_en <= 1'b0;
frame_len <= 0;
tx_data <= 0;
tx_st <= 0;
end
else
begin
case ( tx_st )
0:
begin
tx_vld <= 1'b0;
tx_en <= 1'b0;
tx_data <= 0;
frame_len <= 0;
tx_st <= 1;
end
1:
begin
tx_en <= 1'b0;
tx_vld <= 1'b1;
if ( frame_len == LEN )
begin
tx_vld <= 1'b0;
tx_st <= 3;
end
else if ( tx_rdy )
begin
tx_en <= 1'b1;
tx_st <= 2;
end
end
2:
begin
tx_en <= 1'b0;
frame_len <= frame_len + 1;
tx_data <= tx_data + 1;
tx_st <= 1;
end
3:
begin
frame_len <= 0;
tx_vld <= 1'b0;
tx_en <= 1'b0;
if ( tx_vld_ack )
tx_st <= 1;
end
default:
tx_st <= 0;
endcase
end
hs_txrx
#(
.IDLE (IDLE)
)
hs_txrx_inst
(
.inclk (inclk),
.pll_rst (pll_rst),
.tx_rdy (tx_rdy),
.tx_vld (tx_vld),
.tx_data (tx_data),
.tx_en (tx_en),
.tx_vld_ack (tx_vld_ack),
`ifdef SIM
.tx (tx_rx),
.rx (tx_rx),
`else
.tx (tx),
.rx (rx),
`endif
.rx_data_vld(rx_data_vld),
.rx_data_rdy(rx_data_rdy),
.ko (ko),
.rx_data (rx_data),
.clk_100M (clk_100M),
.pll_locked (pll_locked)
);
endmodule
打开SIM宏后,仿真结果如图3所示:

图3
图3中显示出修改后仿真结果和之前课程介绍的一致。
2.增加recv_data_link.v模块
之前的工程中PCS层的信号直接穿透到应用层。本节内容添加了接收端的数据链路层模块,代码如下:
module recv_data_link
#(
parameter SYNC_START1 = 10'b0011111010, //K28.5 with dipin=0;
parameter SYNC_START2 = 10'b1100000101, //K28.5 with dispin=1
parameter SYNC_END1 = 10'b0011111001, //K28.1 with dipin=0;
parameter SYNC_END2 = 10'b1100000110 //K28.1 with dispin=1
)
(
input rst,
input clk_100M ,
input clk_400M ,
input rx,
output rx_ko,
output [ 7: 0 ] rx_data,
output rx_data_rdy,
output rx_data_vld
);
wire phy_rx_ko;
wire [ 7: 0 ] phy_rx_data;
wire phy_rx_data_rdy;
wire phy_rx_data_vld;
assign rx_ko = phy_rx_ko;
assign rx_data = phy_rx_data;
assign rx_data_rdy = phy_rx_data_rdy;
assign rx_data_vld = phy_rx_data_vld;
recv_data_pcs #
(
.SYNC_START1 ( 10'b0011111010 ), //K28.5 with dipin=0;
.SYNC_START2 ( 10'b1100000101 ), //K28.5 with dispin=1
.SYNC_END1 ( 10'b0011111001 ), //K28.1 with dipin=0;
.SYNC_END2 ( 10'b1100000110 ) //K28.1 with dispin=1
)
recv_data_pcs_inst
(
.rst ( rst ),
.clk_100M ( clk_100M ),
.clk_400M ( clk_400M ),
.rx ( rx ),
.pcs_rx_data_rdy ( phy_rx_data_rdy ),
.pcs_ko ( phy_rx_ko ),
.pcs_rx_data ( phy_rx_data ),
.pcs_rx_data_vld ( phy_rx_data_vld )
);
endmodule
3.hs_rxtx.v模块修改
因为接收端新添加了数据链路层,所以hs_txrx.v模块也需要做出相应的修改,代码如下:
module hs_txrx
#(
parameter IDLE = 10'b1010_1010_10
)
(
input inclk,
input pll_rst,
output tx_rdy,
input tx_vld,
input [ 7: 0 ] tx_data,
input tx_en,
output tx_vld_ack,
output tx,
input rx,
output rx_data_vld,
output rx_data_rdy,
output ko,
output [ 7: 0 ] rx_data,
output clk_100M,
output pll_locked
);
wire clk_400M;
wire rst = !pll_locked;
trans_data_link
#(
.IDLE ( IDLE )
)
trans_data_link_inst
(
.rst ( rst ),
.clk_100M ( clk_100M ),
.tx_rdy ( tx_rdy ),
.tx_vld ( tx_vld ),
.tx_data ( tx_data ),
.tx_en ( tx_en ),
.tx_vld_ack ( tx_vld_ack ),
.tx_out ( tx )
);
recv_data_link #
(
.SYNC_START1 ( 10'b0011111010 ), //K28.5 with dipin=0;
.SYNC_START2 ( 10'b1100000101 ), //K28.5 with dispin=1
.SYNC_END1 ( 10'b0011111001 ), //K28.1 with dipin=0;
.SYNC_END2 ( 10'b1100000110 ) //K28.1 with dispin=1
)
recv_data_link_inst
(
.rst ( rst ),
.clk_100M ( clk_100M ),
.clk_400M ( clk_400M ),
.rx ( rx ),
.rx_data_rdy ( rx_data_rdy ),
.rx_ko ( ko ),
.rx_data ( rx_data ),
.rx_data_vld ( rx_data_vld )
);
pll1 pll1_inst
(
.areset ( pll_rst ), //1'b0;
.inclk0 ( inclk ),
.c0 ( clk_100M ),
.c1 ( clk_400M ),
.locked ( pll_locked )
);
endmodule
4.增加管脚约束

图4-1
注意图4-1中分配管脚时,tx和tx(n)的I/O Standard不是LVDS而是LVDS_E_3R。LVDS_E_3R中的“E”表示Emulated LVDS,“3R”指Emulated LVDS外接电阻网络。
The Intel Cyclone 10 LP left and right I/O banks (row I/Os) support true LVDS transmitters. The top and bottom I/O banks support emulated LVDS transmitters with external resistors.

图4-2
本工程使用的的LVDS输出,输入方式。 所以,我们需要对管脚进行约束。包括inclk, pll_rst, tx (txn), rx (rxn)。 其中tx , rx 为LVDS标准。
根据FII-PRA006 原理图,我们使用:
- FII-PRA006板载时钟,
- 开发板中的down key 作为pll_rst 信号。
- PMOD GPIO2 接口 IO8_p,IO8_n,作为tx 的LVDS
- PMOD GPIO2 接口 IO10_p,IO10_n,作为rx 的LVDS

图5
注意: 在选择LVDS的时候, 需要将FII-PRA006的硬件FPGA Bank 电压设置为2.5V。

图6

图7

图8
查看原理图,根据图5-图8 ,确定FPGA的输入输出管脚。
在板载实验中, 我们使用杜邦线,链接tx,rx LVDS对。在一块FPGA板上实现LVDS高速通信, 当然,也可以使用2块FPGA板,使用杜邦线链接。 注意, 在距离比较远的2个板子之间通信, 尽量使用双绞线。

图9
5.添加SignalTap Logic Analyzer

图10
在SignalTap 中增加我们需要测试的信号,包括:
- tx_rdy
- tx_vld
- tx_data[7:0]
- tx_en
- tx_vld_ack
- rx_data[7:0]
- rx_data_rdy
- ko
- rx_data_vld
- rx
- tx
同时设置trigger 条件为 tx_vld。

图11
在SignalTap 采样时, 我们使用clk_100M 时钟, 采样深度为1k。
6.板载测试结果
FII-PRA006 开发板回环实验

图12
根据图12,1)选择JTAG驱动,2)选择sof文件,3)下载sof文件。

图13
从实际实验的结果中, 可以看出, 发送端的数据被接收端正确的接收。 这一点和仿真程序中测试的结果相同。证明我们设计的整个算法是正确的。
板对板的LVDS高速通讯实验

图14

图15
图15 中可以看出, 在一个包结束的时候, k28.1 的结束标识,不同的包, 产生不同的k28.1 (正码,反码)。

图16
图16 为示波器抓取的LVDS差分信号当中的一根信号,可以看出所发送出来的数据流。
总结:
我们使用了开发板内的回环测试, 和板对板的高速串行通讯实验, 可以看出, 在不同的2个板子之间, 也是可以正常通讯的。有条件的同学可以试试逐步加长传输线的距离, 比如使用5m, 10m , 100m 的线长, 再次观察实验的结果。
Quartus工程下载(Quartus 20.1):
[content_control]
[/content_control]