// sha3_top_shared.v - SHA3/SHAKE wrapper with EXTERNAL (shared) keccak_core.
//
// Identical to sha3_top.v except the internal keccak_core instance is
// replaced by explicit kc_* ports, so several Keccak consumers can share a
// single keccak_core via a phase mux (see mlkem_top). The sponge state
// (mb_state_r) and all FSM/squeeze logic stay here; only the 24-round
// permutation is external. Bit-identical to sha3_top when wired to a
// dedicated keccak_core.
//
// Single-block modes (mb_en=0):
//   00 = G  (SHA3-512):   rate=576,  suffix=01, msg_len=264, out=512
//   01 = H  (SHA3-256):   rate=1088, suffix=01, msg_len=256, out=256
//   10 = J  (SHAKE-256):  rate=1088, suffix=1111,msg_len=512,out=256
//
// Multi-block SHA3-256 (mb_en=1): streams pre-padded 1088-bit rate blocks;
// the CALLER applies SHA3-256 padding to the final block.
//
// Keccak interface (port names mirror the keccak_core port each connects to):
//   kc_state_o[1599:0] - keccak result          (input,  from keccak_core.state_o)
//   kc_valid_o         - keccak output valid     (input,  from keccak_core.valid_o)
//   kc_ready_o         - keccak ready for input  (input,  from keccak_core.ready_o)
//   kc_state_i[1599:0] - keccak input state      (output, to   keccak_core.state_i)
//   kc_valid_i         - request permutation     (output, to   keccak_core.valid_i)
//   kc_ready_i         - accept keccak output    (output, to   keccak_core.ready_i, always 1)

module sha3_top_shared (
    input              clk,
    input              rst_n,
    input  [1:0]       mode,
    input  [511:0]     data_i,
    input              valid_i,
    output             ready_o,
    output [511:0]     hash_o,
    output             valid_o,
    input              ready_i,
    // --- multi-block SHA3-256 absorb (tie mb_en=0 to disable) ---
    input              mb_en,
    input  [1087:0]    mb_block_i,
    input              mb_valid_i,
    input              mb_last_i,
    output             mb_ready_o,
    // --- external (shared) keccak_core interface ---
    input  [1599:0]    kc_state_o,
    input              kc_valid_o,
    /* verilator lint_off UNUSEDSIGNAL */
    input              kc_ready_o,
    /* verilator lint_on UNUSEDSIGNAL */
    output [1599:0]    kc_state_i,
    output             kc_valid_i,
    output             kc_ready_i
);

    // ================================================================
    // FSM state encoding
    // ================================================================
    localparam ST_IDLE    = 2'd0;
    localparam ST_PERMUTE = 2'd1;
    localparam ST_SQUEEZE = 2'd2;

    reg [1:0]  state_r, state_next;

    // ================================================================
    // Absorb state: message || suffix || pad10*1 into rate bits
    // ================================================================
    wire [575:0]  g_pad;
    wire [1087:0] h_pad;
    wire [1087:0] j_pad;

    assign g_pad = {1'b1, {308{1'b0}}, 1'b1, 2'b10, data_i[263:0]};
    assign h_pad = {1'b1, {828{1'b0}}, 1'b1, 2'b10, data_i[255:0]};
    // J: SHAKE suffix is "1111" — all ones, order irrelevant
    assign j_pad = {1'b1, {570{1'b0}}, 1'b1, 4'b1111, data_i[511:0]};

    wire [1599:0] absorb_state;
    assign absorb_state = (mode == 2'b00) ? {{(1600-576){1'b0}}, g_pad} :
                          (mode == 2'b01) ? {{(1600-1088){1'b0}}, h_pad} :
                          (mode == 2'b10) ? {{(1600-1088){1'b0}}, j_pad} :
                          1600'd0;

    // ================================================================
    // Multi-block SHA3-256 absorb FSM (active only when mb_en=1)
    // ================================================================
    localparam MB_IDLE    = 2'd0;   // ready for a block (or first block)
    localparam MB_PERMUTE = 2'd1;   // keccak running on xored state
    localparam MB_DONE    = 2'd2;   // squeeze: present 256-bit digest

    reg  [1:0]    mb_state, mb_state_next;
    reg  [1599:0] mb_state_r;        // running sponge state
    reg           mb_last_r;         // captured last-block flag
    reg  [255:0]  mb_digest_r;       // latched 256-bit digest (sticky in MB_DONE)

    // XOR the incoming block into the low 1088 bits (rate) of the state.
    wire [1599:0] mb_xored;
    assign mb_xored = mb_state_r ^ {{(1600-1088){1'b0}}, mb_block_i};

    // Accept a block only in MB_IDLE while enabled.
    assign mb_ready_o = mb_en && (mb_state == MB_IDLE);
    wire   mb_accept  = mb_en && (mb_state == MB_IDLE) && mb_valid_i;
    wire   mb_kc_valid = mb_accept;   // start keccak on the accept cycle

    always @(*) begin
        mb_state_next = mb_state;
        case (mb_state)
            MB_IDLE:    if (mb_accept)   mb_state_next = MB_PERMUTE;
            MB_PERMUTE: if (kc_valid_o)  mb_state_next = mb_last_r ? MB_DONE : MB_IDLE;
            MB_DONE:    if (ready_i)      mb_state_next = MB_IDLE;
            default:    mb_state_next = MB_IDLE;
        endcase
    end

    // ================================================================
    // External keccak_core interface (was internal keccak_core)
    // ================================================================
    // Keccak input: multi-block xored state when mb_en, else single-block absorb.
    assign kc_state_i = mb_en ? mb_xored : absorb_state;
    // Always accept keccak output (matches the dedicated-core ready_i=1'b1).
    assign kc_ready_i = 1'b1;
    // kc_valid_i: single-block start (state_next==PERMUTE) OR multi-block accept.
    assign kc_valid_i = mb_en ? mb_kc_valid : (state_next == ST_PERMUTE);

    // ================================================================
    // FSM combinational logic
    // ================================================================
    assign ready_o = !mb_en && (state_r == ST_IDLE);

    always @(*) begin
        state_next = state_r;
        case (state_r)
            ST_IDLE:    if (valid_i && ready_o)  state_next = ST_PERMUTE;
            ST_PERMUTE: if (kc_valid_o)           state_next = ST_SQUEEZE;
            ST_SQUEEZE: if (valid_o && ready_i)   state_next = ST_IDLE;
            default:    state_next = ST_IDLE;
        endcase
    end

    // ================================================================
    // Output
    // ================================================================
    reg [511:0] squeezed_state_r;

    // valid_o / hash_o serve both paths, selected by mb_en.
    assign valid_o = mb_en ? (mb_state == MB_DONE) : (state_r == ST_SQUEEZE);
    assign hash_o  = mb_en ? {256'b0, mb_digest_r} : squeezed_state_r;

    // ================================================================
    // Sequential logic
    // ================================================================
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            state_r          <= ST_IDLE;
            squeezed_state_r <= 512'd0;
            mb_state         <= MB_IDLE;
            mb_state_r       <= 1600'd0;
            mb_last_r        <= 1'b0;
            mb_digest_r      <= 256'd0;
        end else begin
            state_r  <= state_next;
            mb_state <= mb_state_next;

            // --- single-block: latch squeezed output ---
            if (state_r == ST_PERMUTE && kc_valid_o) begin
                squeezed_state_r <= kc_state_o[511:0];
            end

            // --- multi-block: capture last flag on accept ---
            if (mb_accept) begin
                mb_last_r <= mb_last_i;
            end

            // --- multi-block: latch permuted state when keccak finishes ---
            if (mb_state == MB_PERMUTE && kc_valid_o) begin
                mb_state_r <= kc_state_o;
                if (mb_last_r) mb_digest_r <= kc_state_o[255:0];
            end

            // --- multi-block: clear running state after digest consumed ---
            if (mb_state == MB_DONE && ready_i) begin
                mb_state_r <= 1600'd0;
            end
        end
    end

endmodule
