本文基于IEEE Std 802.3-2022 PCS 章节,介绍几种66b同步方法,处理位宽包括64bit数据到32bit数据,并给出一些优化方案。
整个简化流程可以表示为:
64bit输入数据->增加同步头->转换成64bit位宽数据-----> PMA tx ----->PMA rx ---->66b同步->去同步头->64bit数据
在上述流程中,没有包括加解扰,也没有包括FEC编解码及帧同步,这些内容后续再补充。PMA属于模拟部分,也不包括。
1.整体框架
假设发送模块输入信号分别为 i_control, i_data, i_frame_first, i_valid。这三个信号分别表示当前数据包含控制符(影响同步头),输入数据,起始帧指示(32拍64bit信号作为一帧),数据有效指示信号。模块输出数据为o_data,o_valid,此时输出数据为纯数据流。
解释一下为什么需要i_frame_first 这个信号,由于32拍数据会插入64bit同步头,说明下一拍输入数据valid数据要保持低电平并且输出缓存下来的第33个64bit数据,所以是33拍数据一个循环。如果没有这个i_frame_first 指示信号根据33拍的周期重复校准,一旦内部计数器有扰动就再也无法和输入数据对齐了。这个问题我们可以在后续代码中查看。
// PCS TX Module with 64B/66B Encoder + Gearbox (66b -> 64b)
// Input: 64-bit per cycle, with control/frame info
// Output: 64-bit continuous data stream (post-gearbox)
module pcs_tx_with_gearbox (
input clk,
input rst_n,
// Input interface (frame-based, 64-bit)
input [63:0] i_data,
input i_control,
input i_frame_first,
input i_valid,
// Output interface (pure 64-bit data stream)
output reg [63:0] o_data,
output reg o_valid
);
// ==============================
// 1. 64B/66B Encoder
// ==============================
localparam [1:0] SYNC_DATA = 2'b10;
localparam [1:0] SYNC_CONTROL = 2'b01;
reg [65:0] encoded_66b; // [65:64] = sync header
reg enc_valid;
always @(*) begin
if (i_control) begin
encoded_66b = {SYNC_CONTROL, i_data};
end else begin
encoded_66b = {SYNC_DATA, i_data};
end
end
// Register encoder output (1-cycle latency)
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
enc_valid <= 1'b0;
end else begin
enc_valid <= i_valid;
end
end
// ==============================
// 2. Frame Counter (for 32-block frame)
// ==============================
reg [5:0] frame_cnt; // 0 to 31
reg in_frame;
reg insert_gap; // after frame ends, insert 1-cycle gap
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
frame_cnt <= 6'd0;
in_frame <= 1'b0;
insert_gap <= 1'b0;
end else begin
if (i_valid) begin
if (i_frame_first) begin
// Start new frame
in_frame <= 1'b1;
frame_cnt <= 6'd0;
insert_gap <= 1'b0;
end else if (in_frame) begin
if (frame_cnt == 6'd31) begin
// End of frame (32nd block)
in_frame <= 1'b0;
insert_gap <= 1'b1; // schedule gap next cycle
end else begin
frame_cnt <= frame_cnt + 1;
end
end
// If not in frame and no start, stay out
end else begin
// i_valid=0: hold state
end
end
end
// ==============================
// 3. Gearbox: 66b -> 64b
// Use a shift register to accumulate bits
// ==============================
reg [129:0] bit_buffer; // >= 66+64 = 130 bits
reg [7:0] buffer_bits; // number of valid bits in buffer
reg gearbox_ready;
// Push 66b into buffer when valid
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
bit_buffer <= 130'd0;
buffer_bits <= 8'd0;
end else begin
if (enc_valid) begin
bit_buffer <= {bit_buffer[129:66], encoded_66b};
buffer_bits <= buffer_bits + 8'd66;
end
end
end
// Pop 64b when enough bits and not in gap
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
o_data <= 64'd0;
o_valid <= 1'b0;
gearbox_ready <= 1'b0;
end else begin
// Can output if >=64 bits and not forced gap
if (buffer_bits >= 8'd64 && !insert_gap) begin
o_data <= bit_buffer[129:66]; // top 64 bits
o_valid <= 1'b1;
// Shift out 64 bits
bit_buffer <= {bit_buffer[65:0], 64'd0};
buffer_bits <= buffer_bits - 8'd64;
gearbox_ready <= 1'b1;
end else begin
o_valid <= 1'b0;
end
end
end
endmodule
评论