Untitled

Anonymous
plain_text
05/01/2026 2:24 AM
11.1 KB
14
Indexable
`timescale 1ns / 1ps

module I2C_RECEIVER (
    input  wire       CLK,
    input  wire       RSTN,
    input  wire       I2C_SCL,
    input  wire       I2C_SDA,
    input  wire       TRIG,

    output reg        VALID,
    output reg [2:0]  IDX0,
    output reg [2:0]  IDX1,
    output reg [3:0]  NUM0,
    output reg [3:0]  NUM1,
    output reg        EMPTY
);

    // =========================================================
    // Synchronize SCL/SDA into CLK domain
    // Use negedge CLK to avoid post-synthesis timing issue
    // when TB changes I2C_SCL/I2C_SDA near posedge CLK.
    // =========================================================
    (* DONT_TOUCH = "true" *) reg scl_d0, scl_d1;
    (* DONT_TOUCH = "true" *) reg sda_d0, sda_d1;

    wire scl_edge;
    assign scl_edge = (scl_d0 ^ scl_d1);

    // negedge input sampler
    always @(negedge CLK or negedge RSTN) begin
        if (!RSTN) begin
            scl_d0 <= 1'b0;
            scl_d1 <= 1'b0;
            sda_d0 <= 1'b0;
            sda_d1 <= 1'b0;
        end
        else begin
            scl_d0 <= I2C_SCL;
            scl_d1 <= scl_d0;

            sda_d0 <= I2C_SDA;
            sda_d1 <= sda_d0;
        end
    end

    // =========================================================
    // Delay sampling by one CLK after SCL edge
    // This avoids SDA glitch around SCL toggle
    // =========================================================
    reg sample_pending;

    // =========================================================
    // Receive 8-bit frame
    // frame = {header[3:0], data[3:0]}, MSB first
    // =========================================================
    reg [7:0] shift_reg;
    reg [2:0] bit_cnt;

    reg [7:0] frame_now;
    reg [3:0] header_now;
    reg signed [3:0] data_now;

    // =========================================================
    // Address list
    // header == 0000 means data is device ID
    // =========================================================
    reg [3:0] addr_list [0:3];
    reg [2:0] addr_cnt;

    // =========================================================
    // Number count
    // =========================================================
    reg [3:0] num_cnt;

    // =========================================================
    // Top-2 signed number
    // =========================================================
    reg signed [3:0] max0_num;
    reg signed [3:0] max1_num;
    reg [2:0]        max0_idx;
    reg [2:0]        max1_idx;
    reg              has0;
    reg              has1;

    reg signed [3:0] new_num;
    reg [2:0]        new_idx;

    reg match_addr;
    reg valid_hold;

    integer i;

    // =========================================================
    // Main sequential logic
    // =========================================================
    always @(posedge CLK or negedge RSTN) begin
        if (!RSTN) begin
            sample_pending <= 1'b0;

            shift_reg <= 8'd0;
            bit_cnt   <= 3'd0;

            frame_now  <= 8'd0;
            header_now <= 4'd0;
            data_now   <= 4'sd0;

            addr_cnt <= 3'd0;
            num_cnt  <= 4'd0;

            for (i = 0; i < 4; i = i + 1) begin
                addr_list[i] <= 4'd0;
            end

            max0_num <= 4'sd0;
            max1_num <= 4'sd0;
            max0_idx <= 3'd0;
            max1_idx <= 3'd0;
            has0     <= 1'b0;
            has1     <= 1'b0;

            VALID      <= 1'b0;
            valid_hold <= 1'b0;

            IDX0  <= 3'd0;
            IDX1  <= 3'd0;
            NUM0  <= 4'd0;
            NUM1  <= 4'd0;
            EMPTY <= 1'b1;
        end
        else begin
            // -------------------------------------------------
            // VALID behavior
            // Keep same as your behavior-correct version.
            // -------------------------------------------------
            VALID <= 1'b0;

            if (TRIG) begin
                VALID      <= 1'b1;
                valid_hold <= 1'b1;
            end
            else if (valid_hold) begin
                VALID      <= 1'b1;
                valid_hold <= 1'b0;
            end

            // -------------------------------------------------
            // Detect SCL toggle first.
            // Do not sample immediately.
            // Sample SDA one CLK later.
            // -------------------------------------------------
            if (scl_edge) begin
                sample_pending <= 1'b1;
            end
            else if (sample_pending) begin
                sample_pending <= 1'b0;

                // =================================================
                // Receive one stable SDA bit
                // =================================================
                shift_reg <= {shift_reg[6:0], sda_d1};

                if (bit_cnt == 3'd7) begin
                    bit_cnt <= 3'd0;

                    frame_now  = {shift_reg[6:0], sda_d1};
                    header_now = frame_now[7:4];
                    data_now   = frame_now[3:0];

                    // ---------------------------------------------
                    // Address frame
                    // header == 0000
                    // ---------------------------------------------
                    if (header_now == 4'b0000) begin
                        if (addr_cnt < 3'd4) begin
                            addr_list[addr_cnt] <= data_now[3:0];
                            addr_cnt <= addr_cnt + 3'd1;
                        end
                    end

                    // ---------------------------------------------
                    // Data frame
                    // header must match address list
                    // ---------------------------------------------
                    else begin
                        match_addr = 1'b0;

                        for (i = 0; i < 4; i = i + 1) begin
                            if ((i < addr_cnt) && (header_now == addr_list[i])) begin
                                match_addr = 1'b1;
                            end
                        end

                        if (match_addr && (num_cnt < 4'd8)) begin
                            new_num = data_now;
                            new_idx = num_cnt[2:0];

                            num_cnt <= num_cnt + 4'd1;

                            // signed comparison
                            // equal value does not replace,
                            // because smaller index wins.
                            if ((!has0) || (new_num > max0_num)) begin
                                max1_num <= max0_num;
                                max1_idx <= max0_idx;
                                has1     <= has0;

                                max0_num <= new_num;
                                max0_idx <= new_idx;
                                has0     <= 1'b1;
                            end
                            else if ((!has1) || (new_num > max1_num)) begin
                                max1_num <= new_num;
                                max1_idx <= new_idx;
                                has1     <= 1'b1;
                            end
                        end
                    end
                end
                else begin
                    bit_cnt <= bit_cnt + 3'd1;
                end
            end

            // =================================================
            // Keep result outputs updated continuously
            // Same as behavior-correct version.
            // =================================================
            if (!has1) begin
                EMPTY <= 1'b1;

                if (has0) begin
                    IDX0 <= max0_idx;
                    NUM0 <= max0_num[3:0];
                end
                else begin
                    IDX0 <= 3'd0;
                    NUM0 <= 4'd0;
                end

                IDX1 <= 3'd0;
                NUM1 <= 4'd0;
            end
            else begin
                EMPTY <= 1'b0;

                IDX0 <= max0_idx;
                NUM0 <= max0_num[3:0];

                IDX1 <= max1_idx;
                NUM1 <= max1_num[3:0];
            end
        end
    end

endmodule
Editor is loading...
Leave a Comment