全部设计参考达芬奇pro教程五十三和五十四章
GTP IP核
第一步:找到GTP的IP核
第二步:配置IP核第一页
我用的是A7的板子,选GTP,GTP被用于A7系列(GTZ被用于少数V7系列,V7系列大多用GTH,K7常用GTX),它所带载的最大带宽为6.6Gb/s
Include Shared Logic in example design:
包含收发器的QPLL、时钟和复位逻辑等在示例设计中,称为从核
Include Shared Logic in example design:
包含收发器的QPLL、时钟和复位逻辑在内核中,称为主核
从核和主核的区别:可以在示例设计中修改共享逻辑,在实际的设计中,可以使用主核也可以使用从核,但若设计中使用了一个主核后,则其内部便使用了该Quad上的QPLL资源,在使用该Quad上的其他GTP接口时,不能再使用主核,也无需再给从核添加共享逻辑(教程写的有点绕哈哈,我的理解:主核若被使用,主核内部就使用了主核Quad上的QPLL资源,主核的GTP接口使用顺位就靠后)
第三步:配置IP核第二页
我选择Quad0上的一个GTP的tx和另一个GTP上的rx来实现数据传输,在一个Quad下成为数据传输的闭环
初始默认只有一个GTP channel被选择,实现两个channel的闭环传输,需要再自行选择一个GTP的channel,图中画圈处即再选一个GTP channel的操作步骤
基于我所选择的闭环用到了Quad中的两个GTP channel,所以Protocol处选择 aurora 8b10b multi lane 4byte (Aurora 8b10b single lane 4byte可用于一个GTP channel,它的rx接收数据,tx发送数据,数据收发和接收相互独立)
可以选择不同的线速率和编码格式,此处均选择一个常见的较低速率5Gbps用于功能验证,参考时钟为125M(收发器输入时钟,频率必须与开发板上为GTP提供时钟的晶振一致)
GTP的两个参考输入时钟都能连到PLL0和PLL1, 所以在ip核中可以任意选择用PLL0还是PLL1,本次实验收发速率一致,所以RX和TX可以共用一个锁相环,否则必须用两个锁相环,本次开发板的外部时钟是接在GTPREFCLK1,故在ip核中将参考时钟设为GTPREFCLK1,本次开发板用了两个SFP接口,这两个接口分别连接到GTP_X0Y4和GTP_X0Y5
第四步:配置IP核第三页
选的4byte,所以外部数据位宽设置为32bit,编码方式选8b/10b
DRP/System Clock Frequency是动态配置或系统工作时钟,通过DRP可以让设计者根据所选线速率和定义的协议实时调整收发器参数,该时钟可以通过锁相环来配置,本次实验将该时钟设为50M
enable TX Buffer和enable RX Buffer是因为数据不管从TSUSRCLK到XCLK还是反过来,都经过了时钟域的转换,所以TX和RX都要一个缓存区缓存下
第五步:配置IP核第四页
8b/10b编码表中有12个控制字符,以K开头,用于一些控制功能,K码中的comma码用于接收端时钟校准和数据对齐,K28.5(对应用户数据为16‘hbc)最为常见,数据在链路中以串行方式进行传输,所以接收端必须对其进行串并转换
第五页和第六页保持默认设置
GTP IP核的Example design更改
注释掉这部分,因为例程中DRP的输入时钟为差分时钟,A7开发板只带载了一个50M时钟的单端晶振,所以要将DRP的输入时钟改为单端时钟输入,我直接将drp_clk输入,注释掉IBUFDS和BUFG
PL部分
moni数据
moni数据模块用的是之前ddr的模拟数据,将640*480的数据传输到GTP的rx端,然后通过GTP的tx端发送出去
只是改动了时钟,改成了clk_200,200MHz
module moni_data(
input clk_200, //例程里clkout的一个,不用白不用,用在此处
output vs,
output hs,
output de_en ,
output [15:0] data_in
);
reg [11:0] x_cnt=12'd1;//每行传输
reg [11:0] y_cnt=12'd1;//行数
reg [1:0]data_en =1'b0;
reg [15:0] de_cnt =12'd0;
always @(posedge clk_200)
begin
if(x_cnt == 12'd800)
x_cnt <= 12'd1;
else
x_cnt <= x_cnt + 12'd1;
end
always @(posedge clk_200)
begin
if(x_cnt==12'd800)//每行在800时,行数加1
if(y_cnt==12'd525)
y_cnt <= 12'd1;
else
y_cnt <= y_cnt + 12'd1;
else;
end
wire hs_en = ((x_cnt>(12'd160)) && (x_cnt<=(12'd800)))?1:0; //注意一句话不能同时<=和>=
wire vs_en = ((y_cnt>(12'd45)) && (y_cnt<=(12'd525)))?1:0;
//一幅图读完 就重新计数x_cnt y_cnt
always @ (posedge clk_200)
begin
if(hs_en)
if(vs_en)
// if ((x_cnt>( 12'd160)) && (x_cnt<=(12'd800)))
// if((y_cnt>( 12'd45)) && (y_cnt<=(12'd525)))
begin
data_en<= 1'b1;
end
else
begin
data_en <= 1'b0;
end
else
data_en <= 1'b0;
end
always @ (posedge clk_200)
begin
if(de_en==1'b1)
if(de_cnt==12'd639)
de_cnt<=12'd0;
else
de_cnt<=de_cnt+12'd1;
else
de_cnt<=12'd0;
end
assign vs = vs_en;
assign hs = hs_en;
assign de_en=data_en;
assign data_in=(de_en==1)?de_cnt:16'd0;
//自己加的ila测试用
ila_5 ila_5
(
.clk (clk_200) ,
.probe0 (de_en) ,
.probe1 (vs) ,
.probe2 (hs) ,
.probe3 (x_cnt) ,
.probe4 (y_cnt) ,
.probe5 (de_cnt),
.probe6 (data_in)
);
endmodule
clk模块
根据达芬奇pro教程的sfp_hdmi生成的模块
输入50MHz
输出如下:
光口模块
光口模块将接收到的16bit数据转换为32bit数据,然后按照8b10b的通信编码格式将数据发送出去,然后将光口接收到的数据按照8b10b的通信解码格式将数据解析出来,最后将32bit的数据转化为16bit
GTP顶层模块(gt_exdes)
一方面负责与用户(FPGA)进行数据交互,一方面还产生控制光口传输的各种时序,并实现对光口的通信操作,用户可以将IP看作是FPGA与光口交流信息的桥梁,这个桥梁Xilinx已经写好,封装成了一个IP供我们使用,我们只需要根据实际需求对IP进行配置即可
光口编码模块(sfp_encoder)
K码(32’hbc),前24位由用户自定义(随意定义),最后的bc因为采用的K28.5,K28.5的格式就是'hbc(低8位是bc)
fifo阈值设为500,该设置的数小于一行有效数即可,例如640*480,640为一行像素数,设的阈值小于640即可,这样是为了最后一行有数可以读
module sfp_encoder#(
//k28.5是32'bc,最低八位是bc,其余随便设,当K是0001时代表数据有K
//码,k码为0000时代表发送的是原始数据
parameter VS_POSE_DATA1 = 32'h55a101bc,
parameter VS_POSE_DA