// tb_sha3_mb_xsim.v - Multi-block SHA3-256 absorb self-check for sha3_top. // // Streams pre-padded 1088-bit rate blocks (mb_h_blocks.hex) into sha3_top // with mb_en=1, then compares the 256-bit digest against mb_h_expected.hex // (= hashlib.sha3_256(ek) for KAT count=0, ek = 800 bytes -> 6 blocks). // // Run (from project root): // xvlog -sv sync_rtl/sha3/keccak_round.v sync_rtl/sha3/keccak_core.v \ // sync_rtl/sha3/sha3_top.v sync_rtl/sha3/TB/tb_sha3_mb_xsim.v // xelab tb_sha3_mb_xsim -s mb_sim --timescale 1ns/1ps // xsim mb_sim -R `timescale 1ns/1ps module tb_sha3_mb_xsim; parameter BLOCK_FILE = "sync_rtl/sha3/TB/vectors/mb_h_blocks.hex"; parameter EXPECTED_FILE = "sync_rtl/sha3/TB/vectors/mb_h_expected.hex"; parameter MAX_BLOCKS = 16; reg clk = 0; reg rst_n = 0; // single-block ports (unused here, tie off) reg [1:0] mode = 2'b01; reg [511:0] data_i = 0; reg valid_i = 0; wire ready_o; wire [511:0] hash_o; wire valid_o; reg ready_i = 0; // multi-block ports reg mb_en = 1'b1; reg [1087:0] mb_block_i = 0; reg mb_valid_i = 0; reg mb_last_i = 0; wire mb_ready_o; sha3_top dut ( .clk(clk), .rst_n(rst_n), .mode(mode), .data_i(data_i), .valid_i(valid_i), .ready_o(ready_o), .hash_o(hash_o), .valid_o(valid_o), .ready_i(ready_i), .mb_en(mb_en), .mb_block_i(mb_block_i), .mb_valid_i(mb_valid_i), .mb_last_i(mb_last_i), .mb_ready_o(mb_ready_o) ); always #5 clk = ~clk; reg [1087:0] blocks [0:MAX_BLOCKS-1]; reg [255:0] expected_mem [0:0]; reg [255:0] expected; integer nblocks; integer i; // count valid (non-x) lines loaded function integer count_blocks; integer k; begin count_blocks = 0; for (k = 0; k < MAX_BLOCKS; k = k + 1) if (blocks[k] !== {1088{1'bx}}) count_blocks = k + 1; end endfunction initial begin for (i = 0; i < MAX_BLOCKS; i = i + 1) blocks[i] = {1088{1'bx}}; $readmemh(BLOCK_FILE, blocks); $readmemh(EXPECTED_FILE, expected_mem); expected = expected_mem[0]; nblocks = count_blocks(); $display("=== Multi-block SHA3-256 absorb TB ==="); $display("Loaded %0d blocks; expected digest = %064x", nblocks, expected); // reset rst_n = 0; ready_i = 0; repeat (4) @(posedge clk); rst_n = 1; @(posedge clk); // stream blocks — hold valid/last stable across the accept edge // (deassert only after mb_ready_o drops) to avoid sampling races. for (i = 0; i < nblocks; i = i + 1) begin mb_block_i = blocks[i]; mb_last_i = (i == nblocks - 1); mb_valid_i = 1'b1; // wait until module is ready, then one more edge is the accept while (!mb_ready_o) @(posedge clk); @(posedge clk); // accept edge (valid & ready both high) while (mb_ready_o) @(posedge clk); // ready drops => accepted, now safe mb_valid_i = 1'b0; mb_last_i = 1'b0; // wait for this block's permutation to finish (ready again or DONE) while (!mb_ready_o && !valid_o) @(posedge clk); $display(" after block %0d: state[255:0] = %064x", i, dut.mb_state_r[255:0]); end // wait for digest i = 0; while (!valid_o && i < 1000) begin @(posedge clk); i = i + 1; end if (!valid_o) begin $display("FAIL: digest never became valid (timeout)"); $finish; end if (hash_o[255:0] === expected) begin $display("PASS: H(ek) = %064x", hash_o[255:0]); $display("ALL TESTS PASSED"); end else begin $display("FAIL: digest mismatch"); $display(" got = %064x", hash_o[255:0]); $display(" expected = %064x", expected); end ready_i = 1; // ack the digest @(posedge clk); $finish; end // global safety timeout initial begin #100000; $display("FAIL: global timeout"); $finish; end endmodule