// keccak_core.v - Synchronous Keccak-f[1600] core with valid/ready
//
// Runs 24 rounds of keccak_round on the input state.
// valid_i high with ready_o high → load state_i, start 24-round sequence.
// Each round takes 1 cycle. After round 23, valid_o asserted with state_o.
//
// Parameter:
//   ROUNDS = 24 (default)
//
// Interface:
//   clk, rst_n    - clock, active-low reset
//   state_i        - 1600-bit input state
//   valid_i        - start permutation
//   ready_o        - core can accept new input
//   state_o        - 1600-bit output state (after permutation)
//   valid_o        - output is valid
//   ready_i        - consumer accepts output

module keccak_core #(parameter ROUNDS = 24) (
    input              clk,
    input              rst_n,
    input  [1599:0]    state_i,
    input              valid_i,
    output             ready_o,
    output [1599:0]    state_o,
    output             valid_o,
    input              ready_i
);

    // ================================================================
    // Internal registers
    // ================================================================
    reg               busy_r;           // 1 while running permutation
    reg [4:0]         cnt_r;            // round counter (0 to ROUNDS-1)
    reg [1599:0]      state_r;          // current state

    // ================================================================
    // Next-state logic
    // ================================================================
    wire               cnt_is_last;     // cnt_r == ROUNDS-1 ?
    wire [1599:0]      state_next;      // combinational round output

    assign cnt_is_last  = (cnt_r == (ROUNDS - 1));

    // keccak_round: apply one round to current state
    keccak_round u_round (
        .state_i(state_r),
        .round_i(cnt_r),
        .state_o(state_next)
    );

    // ================================================================
    // Valid/ready handshake
    // ================================================================
    // ready_o: accept new input when not busy (or just finishing)
    assign ready_o = !busy_r;

    // valid_o: output valid when we just finished the last round
    // (the final state is already in state_r at the output cycle)
    assign valid_o = busy_r && cnt_is_last;

    // state_o: output the final state
    assign state_o = state_next;  // final state from the last round

    // ================================================================
    // Sequential logic
    // ================================================================
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            busy_r  <= 1'b0;
            cnt_r   <= 5'd0;
            state_r <= 1600'd0;
        end else begin
            if (!busy_r) begin
                // IDLE: wait for valid_i
                if (valid_i) begin
                    state_r <= state_i;
                    cnt_r   <= 5'd0;
                    busy_r  <= 1'b1;
                end
            end else begin
                // BUSY: running permutations
                if (cnt_is_last) begin
                    // Final round: check if output can be accepted
                    if (ready_i) begin
                        busy_r  <= 1'b0;
                        cnt_r   <= 5'd0;
                    end
                    // else: hold until ready_i
                end else begin
                    // Intermediate round: advance state
                    state_r <= state_next;
                    cnt_r   <= cnt_r + 5'd1;
                end
            end
        end
    end

endmodule
