本文基于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_control

输入

1 bit

控制符指示信号。当为高电平时,表示当前 i_data 中包含控制字符;为低时表示 i_data 为普通数据载荷。

i_data

输入

64 bits

并行输入数据。每拍(clock cycle)传输 64 位数据。该数据可能是有效载荷、控制字符或空闲序列,具体含义由 i_controli_valid 共同决定。

i_frame_first

输入

1 bit

帧起始标志。当为高电平时,表示当前拍(cycle)的 i_data 是一帧的第一个 64-bit 数据块。一帧由连续 32 拍(即 32 × 64 = 2048 bits)组成。用于帧对齐、缓冲管理或协议解析。

i_valid

输入

1 bit

数据有效指示。当为高电平时,表示当前 i_data 及其伴随信号(i_control, i_frame_first)有效;为低时,i_data 应被忽略(视为无效或空闲周期)。

o_valid

输出

1 bit

输出有效指示:高电平表示 o_data 当前包含有效编码后的数据块;通常与输入 i_valid 对齐或经内部流水后延迟若干周期。

o_data

输出

64 bit

纯数据流输出:已将控制符按协议要求转换为对应的64B/66B 编码块,供后续扰码或串行化使用。

解释一下为什么需要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