
文章目录
前言
PCS IP是一个软核,是连接FPGA用户逻辑与Serdes硬件结构的桥梁。Lattice Serdes提供了很多灵活的配置,全部可以通过IP配置的方式灵活调用。IP配置可通过三种方式实现,一是IP生成时,UI界面提供了各种配置选项;二是例化IP时,提供了FPGA写入的控制信号和读出的状态信号。三是,IP软核提供了寄存器配置接口,相对更靠近底层的配置和状态可通过寄存器配置接口读取或配置。本文介绍这三种方式。
1.PCS IP接口信号

如图所示,是所有PCS IP相关的接口信号。其中有关复位与时钟的信号上节已经介绍过,关于Client Interface实际上是寄存器接口,在本文第三节介绍。Primary I/O是硬件上与serdes物理介质连接的接口,不过多介绍。这里介绍一些相关的控制信号和状态信号的配置与使用。所介绍的信号是基于实现serdes 8b/10b通信使用的,一些不常使用未经我验证的信号不做介绍。
控制信号,全部是输入信号

状态信号,全部是输出信号

2. IP核的配置
PCS软核中定义了很多寄存器,初始寄存器设置可通过使用 IPexpress 中的自动配置功能实现,即在UI界面中点点点的方式。模块生成器提供了一个自动配置文件,包含了所选模式下quad和通道寄存器的设置。可参考这个文件来进行前端仿真,还可以集成到位流中。当一个自动配置文件集成到位流中,那么在配置过程中,所有的 quad 和通道寄存器将根据自动配置文件定义的值进行设置。如果在工作时,用户需要更改控制寄存器或监控状态寄存器,那么设计中必须包含SCI接口,即Serdes Client Interface,在下一节介绍。

IP配置第一页,配置使能的通道,与是否同时使能Rx与Tx,通道选择主要与硬件原理图指示的连接一致。如图下拉框主要选择实现协议,下一个专题实现以太网会介绍SGMII接口。本系列实现G8B/10B,其余未用过。

IP配置第二页介绍时钟、线速率、位宽的配置。靠上半部分设置发送通道,Max Data Rate设置serdes线速率,要与FPGA硬件支持能力,光模块能力,应用需求适配。LatticeECP3最大支持3.125G。Tx Refclk Source配置发送通道参考时钟源,注意发送与接收不必同源。参考时钟的频率应与所设置的线速率成一定的倍数关系。IP给FPGA逻辑的时钟则由Tx Rate设置的线速率与数据Bus共同决定,一般情况下,要求发送与接收通道设置一致。图中下半部分设置接收通道与发送通道一致。

PreEmphasis设置是否使能预加重,预加重 (TX FIR)即在"发射端"补高频,它的作用像是在出门前先"穿厚一点的衣服"。你知道外面冷(高频会衰减),于是你提前给高频多一些能量。通常实验室环境设置为0即可。TX I/O Termination是两个差分发送线之间的终端电阻,让高速信号在走线和负载上传播时不产生反射,从而保证眼图稳定、减少抖动和误码。当高频信号衰减严重的时候,在接收端配置Equalization以补偿高速信号在走线中被"削弱"的高频成分,让接收端能够恢复出原始的方波。接收端终端电阻也是50欧姆。耦合方式要与硬件涉及匹配,通常选择AC耦合。最下面设置时钟的终端电阻。Tx PLL则是用来监测Tx端pll失锁的标准值,通常无需设置。

IP配置第四页,本页主要上是选一些硬件结构上的多路器,接收端发送端都是设置是否给信号取反,是否使能编码器,是否使能fifo桥。关于下方字对齐设置是否由软核字对齐。是否利用内部的状态机,选择K码。按照上面默认设置。第五页设置CTC FIFO相关内容,用一个小 FIFO 来吸收 Tx 时钟与 Rx 时钟之间的频偏,使得 RX 用户侧的时钟域能够继续稳定读出数据,不会因时钟不一致而出现漏读/多读。保持默认即可。

第六页设置回环模式,下一篇文章细讲。是否包含复位序列,做内回环测试的时候不包含,其余时刻包含。最后是勾选SCI接口以在代码运行过程中读写寄存器。最后一页包含一些生成信息,保持默认即可。
3.寄存器接口
接口信号如下:
| 信号 | 作用 |
|---|---|
| sci_wrdata[7:0] | 要写入寄存器的数据 |
| sci_sel_quad | 选择quad寄存器,高有效 |
| sci_wrn | 要写入寄存器的数据 |
| sci_sel_ch | 写选通,低有效 |
| sci_addr | 地址总线输入,官方文档中有具体映射 |
| sci_rd | 读数据选择 |
| sci_rddata[7:0] | 读数据输出 |
| 操作时序如下: |


下面是我写的寄存器读取的实例代码,供参考。
cpp
module reg_rd (
input clk_fpga,
input rst_n,
output [7:0] sci_wrdata,
output [5:0] sci_addr,
input [7:0] sci_rddata,
output sci_sel_quad,
output sci_rd,
output sci_wrn,
output reg sci_sel_ch2
);
reg [31:0]cnt_1s;
reg start_en;
always @(posedge clk_fpga or negedge rst_n) begin
if(!rst_n)begin
cnt_1s <= 0;
start_en <= 0;
end else begin
cnt_1s <= cnt_1s + 1;
if(cnt_1s == 32'd125_000_000)begin
cnt_1s <= cnt_1s;
start_en <= 1;
end
end
end
//reg [7:0]reg_0[0:5];
reg [7:0]reg_9;
reg [7:0]reg_a;
reg [7:0]reg_b;
reg [7:0]reg_d;
reg [7:0]reg_2;
reg [7:0]reg_21;
reg [7:0]reg_22;
reg [7:0]reg_ch_00;
reg [7:0]reg_ch_01;
reg [7:0]reg_ch_02;
reg [7:0]reg_ch_03;
reg [7:0]reg_ch_04;
reg [7:0]reg_ch_05;
reg [7:0]reg_ch_06;
reg [7:0]reg_ch_07;
reg [7:0]reg_ch_08;
reg [7:0]reg_ch_09;
reg [7:0]reg_ch_0A;
reg [7:0]reg_ch_0B;
reg [7:0]reg_ch_0C;
reg [7:0]reg_ch_0D;
reg [7:0]reg_ch_0E;
reg [7:0]reg_ch_0F;
reg [7:0]reg_ch_10;
reg [7:0]reg_ch_11;
reg [7:0]reg_ch_12;
reg [7:0]reg_ch_13;
reg [7:0]reg_ch_14;
reg [7:0]reg_ch_15;
reg [7:0]reg_ch_16;
reg [7:0]reg_ch_17;
reg [7:0]reg_ch_18;
reg [7:0]reg_ch_19;
reg [7:0]reg_ch_20;
reg [7:0]reg_ch_21;
reg [7:0]reg_ch_22;
reg [7:0]reg_ch_23;
reg [7:0]reg_ch_24;
reg [7:0]reg_ch_25;
reg [7:0]reg_ch_26;
reg [7:0]reg_ch_27;
reg [7:0]reg_ch_28;
reg [7:0]reg_ch_29;
reg [7:0]reg_ch_2A;
reg [7:0]reg_ch_2B;
reg [7:0]reg_ch_2C;
//read monitor
reg [1:0]State;
reg [5:0] sub_addr;
reg end_flag;
always @(posedge clk_fpga or negedge rst_n) begin
if(!rst_n)begin
State <= 0;
end_flag <= 0;
sci_sel_ch2 <= 0;
// sci_rd <= 0;
// sci_sel_quad <= 0;
reg_2 <= 0;
reg_9 <= 0;
reg_a <= 0;
reg_b <= 0;
reg_d <= 0;
reg_21 <= 0;
reg_22 <= 0;
sub_addr<=0;
reg_ch_00 <= 0;
reg_ch_01 <= 0;
reg_ch_02 <= 0;
reg_ch_03 <= 0;
reg_ch_04 <= 0;
reg_ch_05 <= 0;
reg_ch_06 <= 0;
reg_ch_07 <= 0;
reg_ch_08 <= 0;
reg_ch_09 <= 0;
reg_ch_0A <= 0;
reg_ch_0B <= 0;
reg_ch_0C <= 0;
reg_ch_0D <= 0;
reg_ch_0E <= 0;
reg_ch_0F <= 0;
reg_ch_10 <= 0;
reg_ch_11 <= 0;
reg_ch_12 <= 0;
reg_ch_13 <= 0;
reg_ch_14 <= 0;
reg_ch_15 <= 0;
reg_ch_16 <= 0;
reg_ch_17 <= 0;
reg_ch_18 <= 0;
reg_ch_19 <= 0;
reg_ch_20 <= 0;
reg_ch_21 <= 0;
reg_ch_22 <= 0;
reg_ch_23 <= 0;
reg_ch_24 <= 0;
reg_ch_25 <= 0;
reg_ch_26 <= 0;
reg_ch_27 <= 0;
reg_ch_28 <= 0;
reg_ch_29 <= 0;
reg_ch_2A <= 0;
reg_ch_2B <= 0;
reg_ch_2C <= 0;
end else begin
case (State)
0: begin //Cycle 1: Set sci_addr[5:0], sci_sel = 1'b1
// sci_rd <= 0;
if(start_en)begin
State <= sci_sel_ch2 ? 2:1;
end else begin
State <= 0;
end
end
1:begin//Cycle 2: Set sci_rd from 0 1
// sci_rd <= 1;
// sci_sel_quad <= 0;
// State <= 2;
// reg_0[sub_addr]<= sci_rddata;
// if(sci_rddata!=0)begin
// end_flag <= 1;
// end
sub_addr <= sub_addr + 1;
if(sub_addr == 6'h2)begin
reg_2 <= sci_rddata;
end
if(sub_addr == 6'h9)begin
reg_9 <= sci_rddata;
end
if(sub_addr == 6'ha)begin
reg_a <= sci_rddata;
end
if(sub_addr == 6'hb)begin
reg_b <= sci_rddata;
end
if(sub_addr == 6'hd)begin
reg_d <= sci_rddata;
end
if(sub_addr == 6'h21)begin
reg_21 <= sci_rddata;
end
if(sub_addr == 6'h22)begin
reg_22 <= sci_rddata;
end
if(sub_addr==6'd40 )begin
sub_addr <= 0;
State <= 2;
sci_sel_ch2 <= 1;
end else begin
State <= 0;
end
end
2:begin//Obtain reading data from sci_rddata[7:0]
sub_addr <= sub_addr + 1;
case (sub_addr)
6'h0:reg_ch_00 <= sci_rddata;
6'h1:reg_ch_01 <= sci_rddata;
6'h2:reg_ch_02 <= sci_rddata;
6'h3:reg_ch_03 <= sci_rddata;
6'h4:reg_ch_04 <= sci_rddata;
6'h5:reg_ch_05 <= sci_rddata;
6'h6:reg_ch_06 <= sci_rddata;
6'h7:reg_ch_07 <= sci_rddata;
6'h8:reg_ch_08 <= sci_rddata;
6'h9:reg_ch_09 <= sci_rddata;
6'hA:reg_ch_0A <= sci_rddata;
6'hB:reg_ch_0B <= sci_rddata;
6'hC:reg_ch_0C <= sci_rddata;
6'hD:reg_ch_0D <= sci_rddata;
6'hE:reg_ch_0E <= sci_rddata;
6'hF:reg_ch_0F <= sci_rddata;
6'h10:reg_ch_10 <= sci_rddata;
6'h11:reg_ch_11 <= sci_rddata;
6'h12:reg_ch_12 <= sci_rddata;
6'h13:reg_ch_13 <= sci_rddata;
6'h14:reg_ch_14 <= sci_rddata;
6'h15:reg_ch_15 <= sci_rddata;
6'h16:reg_ch_16 <= sci_rddata;
6'h17:reg_ch_17 <= sci_rddata;
6'h18:reg_ch_18 <= sci_rddata;
6'h19:reg_ch_19 <= sci_rddata;
6'h20:reg_ch_20 <= sci_rddata;
6'h21:reg_ch_21 <= sci_rddata;
6'h22:reg_ch_22 <= sci_rddata;
6'h23:reg_ch_23 <= sci_rddata;
6'h24:reg_ch_24 <= sci_rddata;
6'h25:reg_ch_25 <= sci_rddata;
6'h26:reg_ch_26 <= sci_rddata;
6'h27:reg_ch_27 <= sci_rddata;
6'h28:reg_ch_28 <= sci_rddata;
6'h29:reg_ch_29 <= sci_rddata;
6'h2A:reg_ch_2A <= sci_rddata;
6'h2B:reg_ch_2B <= sci_rddata;
6'h2C:reg_ch_2C <= sci_rddata;
default: ;
endcase
if(sub_addr==6'h2C )begin
sub_addr <= 0;
State <= 3;
end else begin
State <= 0;
end
end
3:begin
// sci_rd <= 0;
State <= 3;
end
default:;
endcase
end
end
assign sci_addr = sub_addr;
assign sci_sel_quad =1;
assign sci_wrn = 0;
assign sci_rd = 1;
endmodule
4.传送门
|-----|
| END |
📡文章原创,首发于CSDN论坛。
📡欢迎点赞♥♥收藏⭐⭐打赏💵💵!
📡欢迎评论区或私信指出错误💉,提出宝贵意见或疑问😱。