此次文章主要围绕64B66B自定义PHY层模块编写。
1、PHY层-tx设计
首先确定接口,根据前文描述,由于tx内部存在Gearbox如何将64bit数据以及2bit头数据发送,并且Gearbox内部输出位宽为64bit,所以需要Gearbox缓存2bit数据,由于Gearbox为64bit大小的缓存,这样仅需32个周期后Gearbox数据就会缓存满,这时就需要外部或内部计数器进行Gearbox缓存空间计数。
与GT-module交互的接口:
input i_tx_clk ,
input i_tx_rst ,
output [63:0] o_tx_data ,
output [1 :0] o_tx_header ,
output [6 :0] o_tx_sequence ,
与上层用户交互的接口:
input [63:0] s_axis_data ,
input [7 :0] s_axis_keep ,
input s_axis_last ,
input s_axis_valid ,
output s_axis_ready
在设计时,我们们一定要有一定概念,当我们接收到用户的数据,同时我们需要接收到用户的数据,那就得需要一个蓄水池对用户数据进行缓存。
在我们没思路设计时,首先就将输出寄存器进行编写。
在设计fifo写逻辑时,我们就需要外部计数器驱动gt收发器。
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
ro_sequence <= 6'd0;
else if(ro_sequence == 6'd32)
ro_sequence <= 6'd0;
else
ro_sequence <= ro_sequence+6'd1;
end
随后通过外部计数器驱动fifo写使能。
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
r_fifo_rden <= 1'd0;
else if(w_gt_tx_data_vld)
r_fifo_rden <= 1'd0;
else if(!w_fifo_empty)
r_fifo_rden <= 1'd1;
else
r_fifo_rden <= 1'd0;
end
根据64B/66B设计规则,首先是设置起始帧的设计。根据起始帧格式设计,这时D8数据就会空闲出来,就需要流水线的形式进行设计。 通过状态机进行设计,首先是发送起始帧状态(P_ST_SOF),随后是发送数据(P_ST_DATA),最后是发送结束帧状态(P_ST_EOF)。
这里主要的是发送结束帧状态的分析,这里可以分情况分析:时序图如下所示:
尾部掩码状况 |
编码方式 |
情况分析 |
8’b1000_0000 |
{w_fifo_dout[15:8],w_fifo_dout[23:16],w_fifo_dout[31:24],w_fifo_dout[39:32],w_fifo_dout[47:40],w_fifo_dout[55:48],w_fifo_dout[63:56],r_fifo_dout[7:0]} |
根据编码方式提前一拍就需要进行组尾帧操作 |
8’b1100_0000 | ||
8’b1110_0000 | ||
8’b1111_0000 | ||
8’b1111_1000 | ||
8’b1111_1100 | ||
8’b1111_1110 | ||
8’b1111_1111 |
根据编码方式后一拍就需要进行组尾帧操作 |
整体代码如下:
module PHY_tx(
//模块处理时钟以及复位信号
input i_clk ,
input i_rst ,
//GT module 交互接口
output[6 :0] o_sequence ,
output[63:0] o_tx_data ,
output[1 :0] o_tx_header ,
input i_tx_reset_done ,
//与用户交互接口
input [63:0] i_s_axis_data ,
input [7 :0] i_s_axis_keep ,
input i_s_axis_valid ,
output o_s_axis_ready ,
input i_s_axis_last
);
/***************param********************/
parameter P_ST_INIT = 16'd0,
P_ST_IDLE = 16'd1,
P_ST_SOF = 16'd2,
P_ST_DATA = 16'd3,
P_ST_EOF = 16'd4;
/****************reg*********************/
reg[5 :0] ro_sequence ;
reg[63:0] ro_tx_data ;
reg[1 :0] ro_tx_header ;
reg[7 :0] ri_s_axis_keep ;
reg ro_s_axis_ready ;
reg[15:0] r_usr_data_cnt ;
reg[15:0] r_usr_data_sum ;
reg[15:0] r_rd_fifo_cnt ;
//fifo 相关信号
reg r_fifo_rden ;
reg r_fifo_rden_1d ;
reg[63:0] r_fifo_dout ;
//state
reg[15:0] r_st_current ;
reg[15:0] r_st_next ;
reg[15:0] r_st_cnt ;
/***************wire*********************/
wire[63:0] w_fifo_dout ;
wire w_fifo_empty ;
wire w_fifo_full ;
wire w_fifo_prog_empty;
wire w_gt_tx_data_vld;
/***************assign*******************/
assign o_sequence = {1'b0,ro_sequence};
assign o_tx_data = ro_tx_data ;
assign o_tx_header= ro_tx_header ;
assign o_s_axis_ready= ro_s_axis_ready;
assign w_gt_tx_data_vld = ro_sequence == 6'30 ? 1'b0:1'b1;
/***************always*******************/
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
ro_sequence <= 6'd0;
else if(ro_sequence == 6'd32)
ro_sequence <= 6'd0;
else
ro_sequence <= ro_sequence+6'd1;
end
//对尾部掩码信号进行缓存
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
ri_s_axis_keep <= 8'b0;
else if(i_s_axis_last)
ri_s_axis_keep <= i_s_axis_keep;
else
ri_s_axis_keep <= ri_s_axis_keep;
end
//fifo 写逻辑
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
r_fifo_rden <= 1'd0;
else if(w_gt_tx_data_vld)
r_fifo_rden <= 1'd0;
else if(!w_fifo_empty)
r_fifo_rden <= 1'd1;
else
r_fifo_rden <= 1'd0;
end
//起始帧设计
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
r_st_current <= 16'd0;
else
r_st_current <= r_st_next;
end
always@(*)
begin
case(r_st_current)
P_ST_INIT : r_st_next = i_tx_reset_done ? P_ST_IDLE : P_ST_INIT;
P_ST_IDLE : r_st_next = r_fifo_rden ? P_ST_SOF : P_ST_IDLE;
P_ST_SOF : r_st_next = P_ST_DATA;
P_ST_DATA : r_st_next = ( ((ri_s_axis_keep != 8'b1111_1111 || ri_s_axis_keep != 8'b1111_1110 ) && w_fifo_prog_empty) || ((ri_s_axis_keep == 8'b1111_1111 || ri_s_axis_keep == 8'b1111_1110 ) && w_fifo_empty) ) ? P_ST_EOF : P_ST_DATA;
P_ST_EOF : r_st_next = P_ST_IDLE;
default : r_st_next = P_ST_INIT;
endcase
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
r_st_cnt <= 16'd0;
else if(r_st_current != r_st_next)
r_st_cnt <= 16'd0;
else
r_st_cnt <= r_st_cnt+16'd1;
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)begin
ro_tx_data <= 64'd0;
ro_tx_header<= 2'd0 ;
end else if(r_st_current == P_ST_SOF)begin
ro_tx_data <= {w_fifo_dout[15:8],w_fifo_dout[23:16],w_fifo_dout[31:24],w_fifo_dout[39:32],w_fifo_dout[47:40],w_fifo_dout[55:48],w_fifo_dout[63:56],8'h78};
ro_tx_header<= 2'b10;
end else if(r_st_current == P_ST_DATA)begin
ro_tx_data <= {w_fifo_dout[15:8],w_fifo_dout[23:16],w_fifo_dout[31:24],w_fifo_dout[39:32],w_fifo_dout[47:40],w_fifo_dout[55:48],w_fifo_dout[63:56],r_fifo_dout[7:0]};
ro_tx_header<= 2'b01;
end else if(r_st_current == P_ST_EOF)begin
ro_tx_header<= 2'b10;
case(ri_s_axis_keep)
8'b1000_0000:ro_tx_data <= {{5{7'h16}},5'd0,w_fifo_dout[64:56],r_fifo_dout[7:0],8'hAA};
8'b1100_0000:ro_tx_data <= {{4{7'h16}},4'd0,w_fifo_dout[55:48],w_fifo_dout[64:56],r_fifo_dout[7:0],8'hB4};
8'b1110_0000:ro_tx_data <= {{3{7'h16}},3'd0,w_fifo_dout[47:40],w_fifo_dout[55:48],w_fifo_dout[64:56],r_fifo_dout[7:0],8'hCC};
8'b1111_0000:ro_tx_data <= {{2{7'h16}},2'd0,w_fifo_dout[39:32],w_fifo_dout[47:40],w_fifo_dout[55:48],w_fifo_dout[64:56],r_fifo_dout[7:0],8'hD2};
8'b1111_1000:ro_tx_data <= {{1{7'h16}},1'd0,w_fifo_dout[31:24],w_fifo_dout[39:32],w_fifo_dout[47:40],w_fifo_dout[55:48],w_fifo_dout[64:56],r_fifo_dout[7:0],8'hE1};
8'b1111_1100:ro_tx_data <= {w_fifo_dout[23:16]w_fifo_dout[31:24],w_fifo_dout[39:32],w_fifo_dout[47:40],w_fifo_dout[55:48],w_fifo_dout[64:56],r_fifo_dout[7:0],8'hFF};
8'b1111_1110:ro_tx_data <= {{7{7'h16}},7'd0,8'h87};
8'b1111_1111:ro_tx_data <= {{6{7'h16}},6'd0,r_fifo_dout[7:0],8'h87};
default :ro_tx_data <= 64'd0;
endcase
end else begin
ro_tx_data <= ro_tx_data ;
ro_tx_header<= ro_tx_header;
end
end
/**************component*****************/
FIFO_64X512 FIFO_64X512_U0 (
.clk (i_clk ),
.din (i_s_axis_data ),
.wr_en (i_s_axis_valid && ro_s_axis_ready),
.rd_en (r_fifo_rden ),
.dout (w_fifo_dout ),
.full (w_fifo_full ),
.empty (w_fifo_empty ),
.prog_empty (w_fifo_prog_empty )
);
endmodule
2、PHY层-rx设计
首先在进行设计时,我们需要知道这个模块需要干什么,具体的功能是什么。这个模块的具体功能是:承下启上,接收gt-module的数据转换成上层用户接口的数据。这样就确认的模块的数据接口:
module PHY_rx(
input i_clk , //系统时钟
input i_rst , //系统复位
//用户接口
output [63:0] o_m_axis_data ,
output [7 :0] o_m_axis_keep ,
output o_m_axis_last ,
output o_m_axis_valid ,
//gt接口
input [63:0] i_gt_rx_data ,
input [1 :0] i_gt_rx_header ,
input i_gt_rx_valid ,
input i_gt_rx_header_valid
);
随后就该想既然是承下,那么就得通过GT-Module中的数据中识别出起始数据,纯数据以及结束数据。
当我们识别了起始帧数据后,那这时我们就需要流水线的方式进行数据处理,与数据拼接。
这里重点讲一下,重点讲解一下8种不同的结尾帧的掩码情况。
尾部帧头情况 |
编码方式 |
情况分析 |
掩码情况 |
8’h87 |
{ri_gt_rx_data_1d[15:8],ri_gt_rx_data_1d[23:16],ri_gt_rx_data_1d[31:24],ri_gt_rx_data_1d[39:32],ri_gt_rx_data_1d[47:40],ri_gt_rx_data_1d[55:48],ri_gt_rx_data_1d[63:56],ri_gt_rx_data[7:0]} |
当前ri_gt_rx_data_1d中内部以及fi_gt_rx_data没有后续的数据,但提前一拍中是有ri_gt_rx_data_1d的[7:0]的无效数据。 |
8’b1111_1110,提前一拍 |
8’h99 |
当前ri_gt_rx_data_1d中内部以及fi_gt_rx_data没有后续的数据,但提前一拍中是有ri_gt_rx_data_1d的[15:8]的有效数据,替换到,ri_gt_rx_data[7:0]这个位置 |
8’b1111_1111,提前一拍 | |
8’haa |
当前ri_gt_rx_data_1d中内部以及fi_gt_rx_data没有后续的数据,但提前一拍中是有ri_gt_rx_data_1d的[15:8]的有效数据,替换到,ri_gt_rx_data[7:0]这个位置;当前一拍,ri_gt_rx_data_1d[23:16]是有效数据。 |
8’b1000_0000,当前一拍 | |
8’hb4 |
当前ri_gt_rx_data_1d中内部以及fi_gt_rx_data没有后续的数据,但提前一拍中是有ri_gt_rx_data_1d的[15:8]的有效数据,替换到,ri_gt_rx_data[7:0]这个位置;当前一拍,ri_gt_rx_data_1d[31:16]是有效数据。 |
8’b1100_0000,当前一拍 | |
8’hcc |
当前ri_gt_rx_data_1d中内部以及fi_gt_rx_data没有后续的数据,但提前一拍中是有ri_gt_rx_data_1d的[15:8]的有效数据,替换到,ri_gt_rx_data[7:0]这个位置;当前一拍,ri_gt_rx_data_1d[39:16]是有效数据。 |
8’b1110_0000,当前一拍 | |
8’hd2 |
当前ri_gt_rx_data_1d中内部以及fi_gt_rx_data没有后续的数据,但提前一拍中是有ri_gt_rx_data_1d的[15:8]的有效数据,替换到,ri_gt_rx_data[7:0]这个位置;当前一拍,ri_gt_rx_data_1d[47:16]是有效数据。 |
8’b1111_0000,当前一拍 | |
8’he1 |
当前ri_gt_rx_data_1d中内部以及fi_gt_rx_data没有后续的数据,但提前一拍中是有ri_gt_rx_data_1d的[15:8]的有效数据,替换到,ri_gt_rx_data[7:0]这个位置;当前一拍,ri_gt_rx_data_1d[55:16]是有效数据。 |
8’b1111_1000,当前一拍 | |
8’hff |
当前ri_gt_rx_data_1d中内部以及fi_gt_rx_data没有后续的数据,但提前一拍中是有ri_gt_rx_data_1d的[15:8]的有效数据,替换到,ri_gt_rx_data[7:0]这个位置;当前一拍,ri_gt_rx_data_1d[63:16]是有效数据。 |
8’b1111_1100,当前一拍 |
整体代码如下:
module PHY_rx(
input i_clk , //系统时钟
input i_rst , //系统复位
//用户接口
output [63:0] o_m_axis_data ,
output [7 :0] o_m_axis_keep ,
output o_m_axis_last ,
output o_m_axis_valid ,
//gt接口
input [63:0] i_gt_rx_data ,
input [1 :0] i_gt_rx_header ,
input i_gt_rx_valid ,
input i_gt_rx_header_valid
);
/*******************reg*************************/
reg [63:0] ri_gt_rx_data ;
reg [63:0] ri_gt_rx_data_1d;
reg [63:0] ro_m_axis_data ;
reg ro_m_axis_valid ;
reg [7 :0] ro_m_axis_keep ;
reg ro_m_axis_last ;
reg r_sof ;
reg r_eof ;
reg [7 :0] r_eof_location ;
/*******************wire************************/
wire w_sof ;//起始标志
wire w_eof ;//结束标志
wire [7 :0] w_eof_location ; //结束标志位置
/******************assign***********************/
assign w_sof = (i_gt_rx_header == 2'b10) && i_gt_rx_header_valid && (i_gt_rx_data[7:0] == 8'h78) && i_gt_rx_header_valid && i_gt_rx_valid;
assign w_eof = (i_gt_rx_header == 2'b10) && i_gt_rx_header_valid && i_gt_rx_valid && (i_gt_rx_data[7:0] == 8'h87 ||
i_gt_rx_data[7:0] == 8'h99 ||
i_gt_rx_data[7:0] == 8'hAA ||
i_gt_rx_data[7:0] == 8'hB4 ||
i_gt_rx_data[7:0] == 8'hCC ||
i_gt_rx_data[7:0] == 8'hD2 ||
i_gt_rx_data[7:0] == 8'hE1 ||
i_gt_rx_data[7:0] == 8'hFF) ;
assign w_eof_location = (i_gt_rx_header == 2'b10) && i_gt_rx_header_valid && i_gt_rx_header_valid && (i_gt_rx_data[7:0] == 8'h87) && i_gt_rx_valid : 8'b0000_0000:
(i_gt_rx_header == 2'b10) && i_gt_rx_header_valid && i_gt_rx_header_valid && (i_gt_rx_data[7:0] == 8'h99) && i_gt_rx_valid : 8'b1000_0000:
(i_gt_rx_header == 2'b10) && i_gt_rx_header_valid && i_gt_rx_header_valid && (i_gt_rx_data[7:0] == 8'hAA) && i_gt_rx_valid : 8'b1100_0000:
(i_gt_rx_header == 2'b10) && i_gt_rx_header_valid && i_gt_rx_header_valid && (i_gt_rx_data[7:0] == 8'hB4) && i_gt_rx_valid : 8'b1110_0000:
(i_gt_rx_header == 2'b10) && i_gt_rx_header_valid && i_gt_rx_header_valid && (i_gt_rx_data[7:0] == 8'hCC) && i_gt_rx_valid : 8'b1111_0000:
(i_gt_rx_header == 2'b10) && i_gt_rx_header_valid && i_gt_rx_header_valid && (i_gt_rx_data[7:0] == 8'hD2) && i_gt_rx_valid : 8'b1111_1000:
(i_gt_rx_header == 2'b10) && i_gt_rx_header_valid && i_gt_rx_header_valid && (i_gt_rx_data[7:0] == 8'hE1) && i_gt_rx_valid : 8'b1111_1100:
(i_gt_rx_header == 2'b10) && i_gt_rx_header_valid && i_gt_rx_header_valid && (i_gt_rx_data[7:0] == 8'hFF) && i_gt_rx_valid : 8'b1111_1110:
8'b1111_1111;
assign o_m_axis_valid = ro_m_axis_valid;
assign o_m_axis_data = ro_m_axis_data;
assign o_m_axis_keep = ro_m_axis_keep;
assign o_m_axis_last = ro_m_axis_last;
/******************always***********************/
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)begin
ri_gt_rx_data <= 64'd0;
ri_gt_rx_data_1d<= 64'd0;
r_sof <= 1'd0 ;
r_eof <= 1'd0 ;
r_eof_location <= 8'd0 ;
end else begin
ri_gt_rx_data <= i_gt_rx_data;
ri_gt_rx_data_1d<= ri_gt_rx_data;
r_sof <= w_sof;
r_eof <= w_eof;
r_eof_location <= w_eof_location;
end
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst) begin
ro_m_axis_data <= 64'd0;
end else if(r_sof) begin
ro_m_axis_data <= {ri_gt_rx_data_1d[15:8],ri_gt_rx_data_1d[23:16],ri_gt_rx_data_1d[31:24],ri_gt_rx_data_1d[39:32],ri_gt_rx_data_1d[47:40],ri_gt_rx_data_1d[55:48],ri_gt_rx_data_1d[63:56],ri_gt_rx_data[7:0]};
end else if(ro_m_axis_valid) begin
ro_m_axis_data <= {ri_gt_rx_data_1d[15:8],ri_gt_rx_data_1d[23:16],ri_gt_rx_data_1d[31:24],ri_gt_rx_data_1d[39:32],ri_gt_rx_data_1d[47:40],ri_gt_rx_data_1d[55:48],ri_gt_rx_data_1d[63:56],ri_gt_rx_data[7:0]};
end else if((w_eof && ( (w_eof_location == 8'b0000_0000) || (w_eof_location == 1000_0000) ))) begin
case(w_eof_location)
8'b0000_0000: ro_m_axis_data <= {ri_gt_rx_data[15:8],ri_gt_rx_data[23:16],ri_gt_rx_data[31:24],ri_gt_rx_data[39:32],ri_gt_rx_data[47:40],ri_gt_rx_data[55:48],ri_gt_rx_data[63:56],8'd0};
8'b1000_0000: ro_m_axis_data <= {ri_gt_rx_data[15:8],ri_gt_rx_data[23:16],ri_gt_rx_data[31:24],ri_gt_rx_data[39:32],ri_gt_rx_data[47:40],ri_gt_rx_data[55:48],ri_gt_rx_data[63:56],i_gt_rx_data[15:8]};
default : ro_m_axis_data <= 64'd0;
end else if(r_eof && !( (r_eof_location == 8'b0000_0000) || (r_eof_location == 1000_0000) ))
case(r_eof_location)
8'b1100_0000: ro_m_axis_data <= {ri_gt_rx_data_1d[15:8],56'd0};
8'b1110_0000: ro_m_axis_data <= {ri_gt_rx_data_1d[15:8],ri_gt_rx_data_1d[23:16],48'd0};
8'b1111_0000: ro_m_axis_data <= {ri_gt_rx_data_1d[15:8],ri_gt_rx_data_1d[23:16],ri_gt_rx_data_1d[31:24],40'd0};
8'b1111_1000: ro_m_axis_data <= {ri_gt_rx_data_1d[15:8],ri_gt_rx_data_1d[23:16],ri_gt_rx_data_1d[31:24],ri_gt_rx_data_1d[39:32],32'd0};
8'b1111_1100: ro_m_axis_data <= {ri_gt_rx_data_1d[15:8],ri_gt_rx_data_1d[23:16],ri_gt_rx_data_1d[31:24],ri_gt_rx_data_1d[39:32],ri_gt_rx_data_1d[31:24],24'd0};
8'b1111_1110: ro_m_axis_data <= {ri_gt_rx_data_1d[15:8],ri_gt_rx_data_1d[23:16],ri_gt_rx_data_1d[31:24],ri_gt_rx_data_1d[39:32],ri_gt_rx_data_1d[31:24],ri_gt_rx_data_1d[23:16],16'd0};
default : ro_m_axis_keep <= 64'd0;
endcase
end else begin
ro_m_axis_data <= ro_m_axis_data;
end
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
ro_m_axis_valid <= 1'd0;
else if((w_eof && ( (w_eof_location == 8'b0000_0000) || (w_eof_location == 1000_0000) )) || (r_eof && !( (r_eof_location == 8'b0000_0000) || (r_eof_location == 1000_0000) )) )
ro_m_axis_valid <= 1'd0;
else if(r_sof)
ro_m_axis_valid <= 1'd1;
else
ro_m_axis_valid <= ro_m_axis_valid;
end
//掩码
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)begin
ro_m_axis_keep <= 8'b1111_1111;
ro_m_axis_last <= 1'd0;
end else if(w_eof && ( (w_eof_location == 8'b0000_0000) || (w_eof_location == 1000_0000) ))begin
ro_m_axis_last <= 1'd1;
case (w_eof_location)
8'b0000_0000: ro_m_axis_keep <= 8'b1111_1110;
8'b1000_0000: ro_m_axis_keep <= 8'b1111_1111;
default : ro_m_axis_keep <= 8'b1111_1111;
endcase
end else if(r_eof && !( (r_eof_location == 8'b0000_0000) || (r_eof_location == 1000_0000) ))begin
ro_m_axis_last <= 1'd1;
case(r_eof_location)
8'b1100_0000: ro_m_axis_keep <= 8'b1000_0000;
8'b1110_0000: ro_m_axis_keep <= 8'b1100_0000;
8'b1111_0000: ro_m_axis_keep <= 8'b1110_0000;
8'b1111_1000: ro_m_axis_keep <= 8'b1111_0000;
8'b1111_1100: ro_m_axis_keep <= 8'b1111_1000;
8'b1111_1110: ro_m_axis_keep <= 8'b1111_1100;
default : ro_m_axis_keep <= 8'b0000_0000;
endcase
end else begin
ro_m_axis_last <= 1'd0;
ro_m_axis_keep <= 8'b1111_1111;
end
end
endmodule