在之前的几节内容中我们研究了通信分层的概念和分层实现的优点,其中在物理层中有一个非常重要的子层PCS(Physical Coding Sublayer)。该层主要有两个功能:
(1)通过编码实现改善信道的质量,如改善直流平衡,提供足够多的时钟信息等;
(2)提供物理层通信控制功能,实现高层协议无法完成的或不需要高层参与完成的功能。如物理层的自动协商,实现物理层差异时的自动适配,如速率适配,线序调换等功能。
本节内容以8b/10b编码编码框架实现PCS子层,工程代码也做了较大修改充分体现了高速通信中分层的概念。高速通信收发层次如图1所示。

图1
由于整个工程的代码层次比较多,本节内容着重以发送端的代码介绍为主,接收端的代码安排在下节内容实现。在本节内容除了PCS子层代码外,其它内容及实现方法已经介绍,但为了通信工程的层次更加清晰,各层代码也做了较大修改,同时应注重研究在同步时钟域下各层握手的实现方法。
tx握手信号如图2所示。

图2-1 tx握手信号

图2-2 tx握手信号
1. 应用层(tb_8b10b.v)
该层在本阶段作为通信部分研究的最高层,其它的通信协议会在后续的章节中作为扩展部分介绍。
`timescale 1 ns / 1 ps
module tb_8b10b
(
);
parameter LEN = 16;
parameter PERIOD = 20 ; //周期20
parameter IDLE = 10'b1010_1010_10;
reg inclk ; // 模拟板级提供的输入时钟
initial
begin
inclk = 1'b0;
#( PERIOD / 2 );
forever
#( PERIOD / 2 ) inclk = ~inclk;
end
reg pll_rst; //实体模块PLL的rst信号
wire tx_rx; // 收发在testbench的回环信号
wire clk_100M;
wire pll_locked; //实体PLL锁定信号
wire rst = !pll_locked; //Testbench内生成复位信号
reg [ 7: 0 ] tx_data; //待发送数据
wire rx_data_vld;
wire rx_data_rdy;
wire ko;
wire [ 7: 0 ] rx_data;
wire tx_rdy;
reg tx_vld;
reg tx_en;
wire tx_vld_ack;
reg [ 7: 0 ] frame_len;
reg [ 2: 0 ] tx_st;
initial
begin
pll_rst = 1'b1;
#50 pll_rst = 1'b0;
end
always@( posedge clk_100M or posedge rst )
if ( rst )
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 ),
.tx ( tx_rx ),
.rx ( tx_rx ),
.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

图3 tb_8b10b.v 仿真波形1

图4 tb_8b10b.v 仿真波形2
2. 高速通信的顶层文件
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_pcs #
(
.SYNC_START1 ( 10'b0011111010 ), //K28.5 with dipin=0;
.SYNC_START2 ( 10'b1100000101 ), //K28.5 with dispin=1
.SYNC_END1 ( 10'b0011111001 ), //K28.5 with dipin=0;
.SYNC_END2 ( 10'b1100000110 ) //K28.5 with dispin=1
)
recv_data_pcs_inst
(
.rst ( rst ),
.clk_100M ( clk_100M ),
.clk_400M ( clk_400M ),
.rx ( rx ),
.pcs_rx_data_rdy ( rx_data_rdy ),
.pcs_ko ( ko ),
.pcs_rx_data ( rx_data ),
.pcs_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
3.发送端数据链路层代码
数据链路层(data-link)的上层是应用层,下层为物理层,直接对接的是物理层的PCS子层。注意上下层之间的握手。同时该层包间隔的设定,为了简化设计,Peamble+SFD部分没有插入,感兴趣的读者可以自己尝试。
module trans_data_link
#(
parameter IDLE = 10'b1010_1010_10
)
(
input rst,
input clk_100M,
output reg tx_rdy,
input tx_vld,
input [ 7: 0 ] tx_data,
input tx_en,
output reg tx_vld_ack,
output tx_out
);
localparam INTER_PAC_INTERVAL= 96; //packect interval
wire tx_phy_rdy;
reg tx_phy_rdy_r;
reg tx_phy_rdy_ack;
always@( posedge clk_100M or posedge rst )
if ( rst )
begin
tx_phy_rdy_r <= 0;
end
else
begin
if ( tx_phy_rdy )
tx_phy_rdy_r <= 1'b1;
else if ( tx_phy_rdy_ack )
tx_phy_rdy_r <= 1'b0;
end
reg [ 7: 0 ] tx_phy_data;
reg tx_phy_en;
reg tx_phy_vld;
reg [ 7: 0 ] pac_interval;
reg [ 2: 0 ] link_st;
always@( posedge clk_100M or posedge rst )
if ( rst )
begin
pac_interval <= 0;
tx_rdy <= 0;
tx_phy_data <= 0;
tx_phy_vld <= 0;
tx_phy_en <= 0;
tx_phy_rdy_ack <= 0;
tx_vld_ack <= 0;
link_st <= 0;
end
else
case ( link_st )
0:
begin
tx_phy_data <= 0;
tx_phy_vld <= 0;
tx_phy_en <= 0;
tx_vld_ack <= 0;
if ( pac_interval == INTER_PAC_INTERVAL)
link_st <= 1;
else
pac_interval <= pac_interval + 1;
end
1:
begin
pac_interval <= 0;
if ( tx_vld )
begin
tx_phy_vld <= 1'b1;
end
if ( tx_phy_rdy )
begin
tx_rdy <= 1'b1;
link_st <= 2;
end
end
2:
begin
tx_rdy <= 1'b0;
if ( tx_en )
begin
tx_phy_data <= tx_data;
link_st <= 3;
end
end
3:
begin
if ( tx_phy_rdy_r )
begin
tx_phy_en <= 1'b1;
tx_phy_rdy_ack <= 1'b1;
link_st <= 4;
end
end
4:
begin
tx_phy_en <= 1'b0;
tx_phy_rdy_ack <= 1'b0;
if ( tx_vld )
begin
tx_rdy <= 1'b1;
link_st <= 2;
end
else
begin
tx_vld_ack <= 1'b1;
link_st <= 0;
end
end
default:
link_st <= 0;
endcase
tx_data_phy
#(
.IDLE( IDLE )
)
tx_data_phy_inst
(
.rst ( rst ),
.clk_100M ( clk_100M ),
.tx_phy_rdy ( tx_phy_rdy ),
.tx_phy_vld ( tx_phy_vld ),
.tx_phy_en ( tx_phy_en ),
.tx_phy_data ( tx_phy_data ),
.tx_out ( tx_out )
);
endmodule

图5 trans_data_link.v 握手信号波形
图5中,应注重观察 data-link 层与应用层及PCS的握手,同时分析包间隔(frame interval)的插入。
4.发送端PCS子层代码
本层的上层是数据链路层,下层为PMA子层,应注意上下层之间的握手。PCS子层采用8b/10b编码实现。其中采用k28.5作为帧起始,K28.1作为帧结束。8b/10b编码部分见 文章 Verilog 8b/10b encoder.

图6
/*
physcal layer :
(1) first layer of physical, PCS sublayer
(2) PCS controled through 8b/10b encode
(3)
engineer: Tim zhuang
company : Fraser innovation Inc.
*/
module tx_data_phy
#(
parameter IDLE = 10'b10_1010_1010
)
(
input rst,
input clk_100M,
input tx_phy_vld,
output reg tx_phy_rdy,
input tx_phy_en,
input [ 7: 0 ] tx_phy_data,
output tx_out
);
reg ki = 0;
reg [ 7: 0 ] datain;
reg dispin;
wire [ 9: 0 ] dataout;
wire dispout;
reg tx_pcs_en;
reg [ 9: 0 ] tx_pcs_data;
reg tx_pcs_rdy;
reg [ 2: 0 ] pcs_st;
always@( posedge clk_100M or posedge rst )
if ( rst )
begin
ki <= 0;
datain <= 0;
dispin <= 0;
tx_pcs_en <= 0;
tx_pcs_data <= 0;
pcs_st <= 0;
end
else
case ( pcs_st )
0:
begin
ki <= 0;
datain <= 0;
dispin <= 0;
tx_pcs_en <= 0;
tx_pcs_data <= 0;
tx_phy_rdy <= 1'b0;
if ( tx_phy_vld )
begin
ki <= 1;
datain <= 8'hBC; //K28.5
pcs_st <= 1;
end
end
1:
begin
tx_pcs_data <= dataout;
tx_phy_rdy <= 1'b0;
tx_pcs_en <= 0;
if ( tx_pcs_rdy )
begin
tx_phy_rdy <= tx_phy_vld;
tx_pcs_en <= 1;
pcs_st <= 2;
end
end
2:
begin
tx_phy_rdy <= 1'b0;
tx_pcs_en <= 0;
if ( ~tx_phy_vld )
pcs_st <= 3;
if ( tx_phy_en )
begin
ki <= 0;
datain <= tx_phy_data;
dispin <= dispout;
pcs_st <= 1;
end
end
3:
begin
ki <= 1;
datain <= 8'h3C; //K28.1
dispin <= dispout;
pcs_st <= 4;
end
4:
begin
tx_pcs_data <= dataout;
tx_phy_rdy <= 1'b0;
tx_pcs_en <= 0;
if ( tx_pcs_rdy )
begin
tx_pcs_en <= 1;
pcs_st <= 0;
end
end
endcase
encode_8b10b encode_8b10b_inst
(
.ki ( ki ),
.datain ( datain ),
.dispin ( dispin ),
.dataout ( dataout ),
.dispout ( dispout )
) ;
reg [ 9: 0 ] tx_pcs_data_r;
reg pcs_data_vld;
wire tx_pma_rdy;
reg [ 9: 0 ] tx_pma_data;
reg tx_pma_en;
always@( posedge clk_100M or posedge rst )
if ( rst )
begin
tx_pma_en <= 1'b0;
tx_pma_data <= IDLE;
tx_pcs_data_r <= IDLE;
tx_pcs_rdy <= 0;
pcs_data_vld <= 0;
end
else
begin
if ( tx_pcs_en )
begin
tx_pcs_data_r <= tx_pcs_data;
pcs_data_vld <= 1'b1;
end
else
tx_pcs_rdy <= 1'b0;
tx_pma_en <= 1'b0;
if ( tx_pma_rdy )
begin
pcs_data_vld <= 1'b0;
tx_pma_en <= 1'b1;
tx_pcs_rdy <= 1'b1;
if ( pcs_data_vld )
tx_pma_data <= tx_pcs_data_r;
else
tx_pma_data <= 10'b10_1010_1010;
end
end
tx_PMA tx_PMA_INST
(
.rst ( rst ),
.clk_100M ( clk_100M ),
.tx_pma_en ( tx_pma_en ),
.tx_pma_data( tx_pma_data ),
.tx_pma_rdy ( tx_pma_rdy ),
.tx_out ( tx_out )
);
endmodule

图7 PCS层结构图

图8 tx_data_phy.v(PCS)仿真波形1
图8中应着重分析与上层 data-link以及下层PMA 层的握手,同时结合本层的程序,研究如何插入K28.5.

图9 tx_data_phy.v(PCS)仿真波形2
利用图9波形结合PCS层代码分析包结束部分的握手,以及如何插入K28.1控制字符。
5.发送端 PMA代码
本层主要实现并串转换,要注意与上层PCS子层的握手。
module tx_PMA
(
input rst,
input clk_100M,
input tx_pma_en,
input [ 9: 0 ] tx_pma_data,
output reg tx_pma_rdy,
output tx_out
);
wire tx_in;
reg [ 9: 0 ] tx_pma_data_r;
reg [ 9: 0 ] tx_sft;
reg [ 1: 0 ] tx_pma_st;
reg [ 3: 0 ] sft_count;
assign tx_in = tx_sft[ 9 ];
always@( posedge clk_100M or posedge rst )
if ( rst )
begin
tx_sft <= 0;
tx_pma_st <= 0;
sft_count <= 0;
tx_pma_data_r <= 0;
tx_pma_rdy <= 0;
end
else
begin
if ( tx_pma_en )
begin
tx_pma_data_r <= tx_pma_data;
end
tx_pma_rdy <= 0;
if ( sft_count == 9 )
begin
tx_pma_rdy <= 1'b1;
tx_sft <= tx_pma_data_r;
sft_count <= 0;
end
else
begin
sft_count <= sft_count + 1;
tx_sft <= { tx_sft[ 8: 0 ], 1'b0 }; //for debug convenience purpose
end
end
tx_PMD tx_PMD_inst
(
.tx_in ( tx_in ),
.tx_out( tx_out )
);
endmodule

图10 tx_pma.v 波形
在图10中注意观察握手,以及上层如何调度IDLE和数据包的。
6. 发送端PMD代码
本层采用LVDS实现PMD(Physical Media Dependent),物理媒介可以是PCB板的差分线,或双绞线。
module tx_PMD
(
input tx_in,
output tx_out
);
IO_TX IO_TX_INST
(
.tx_in ( tx_in ),
.tx_out ( tx_out )
);
endmodule
以上6个部分的设计细节,由于过于繁琐,这里没有一一介绍,感兴趣的同学可以观看对应的视频。