[kmac/fpv] Add keccak_round FPV tb
Add testbench to test SHA3-256 Digest for the empty data.
It compares the masked version and unmasked version results.
Signed-off-by: Eunchan Kim <eunchan@opentitan.org>
diff --git a/hw/ip/kmac/fpv/keccak_round_fpv.core b/hw/ip/kmac/fpv/keccak_round_fpv.core
new file mode 100644
index 0000000..1175680
--- /dev/null
+++ b/hw/ip/kmac/fpv/keccak_round_fpv.core
@@ -0,0 +1,27 @@
+CAPI=2:
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+name: "lowrisc:fpv:keccak_round_fpv:0.1"
+description: "Keccak_f FPV target"
+filesets:
+ files_formal:
+ depend:
+ - lowrisc:prim:all
+ - lowrisc:ip:kmac
+ files:
+ - tb/keccak_round_fpv.sv
+ file_type: systemVerilogSource
+
+targets:
+ default: &default_target
+ # note, this setting is just used
+ # to generate a file list for jg
+ default_tool: icarus
+ filesets:
+ - files_formal
+ toplevel:
+ - keccak_round_fpv
+
+ formal:
+ <<: *default_target
diff --git a/hw/ip/kmac/fpv/tb/keccak_round_fpv.sv b/hw/ip/kmac/fpv/tb/keccak_round_fpv.sv
new file mode 100644
index 0000000..97f044b
--- /dev/null
+++ b/hw/ip/kmac/fpv/tb/keccak_round_fpv.sv
@@ -0,0 +1,209 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+//
+// Testbench module for keccak_f. Intended to be used with a formal tool.
+
+module keccak_round_fpv #(
+ parameter int Width = 1600
+) (
+ input clk_i,
+ input rst_ni,
+ input valid_i,
+ input rand_valid_i,
+ input [Width-1:0] rand_i,
+ output logic done_o
+);
+
+ localparam int W = Width/25;
+ localparam int L = $clog2(W);
+ localparam int NumRound = 12 + 2*L; // Keccak-f only
+ localparam int RndW = $clog2(NumRound+1);
+
+ // Feed parameters
+ localparam int DInWidth = 64; // currently only 64bit supported
+ localparam int DInEntry = Width / DInWidth;
+ localparam int DInAddr = $clog2(DInEntry);
+
+ logic [Width-1:0] masked_state [2];
+ logic [Width-1:0] masked_state_d [2];
+ logic [Width-1:0] unmasked_state [1];
+ logic [Width-1:0] unmasked_state_d[1];
+
+ // Data input
+ logic msg_valid;
+ logic [DInAddr-1:0] msg_addr;
+ logic [DInWidth-1:0] msg_data;
+
+ logic run, clear, masked_complete, unmasked_complete;
+
+ // Masked Keccak round
+
+ keccak_round #(
+ .Width (Width),
+ .EnMasking (1),
+ .ReuseShare (0)
+ ) u_masked (
+ .clk_i,
+ .rst_ni,
+
+ .valid_i (msg_valid),
+ .addr_i (msg_addr),
+ .data_i ({'0, msg_data}),
+
+ .run_i (run),
+ .rand_valid_i (1'b1),
+ .rand_data_i ('0),
+ .rand_consumed_o (),
+
+ .complete_o (masked_complete),
+ .state_o (masked_state),
+ .clear_i (clear)
+ );
+
+ keccak_round #(
+ .Width (Width),
+ .EnMasking (0)
+ ) u_unmasked (
+ .clk_i,
+ .rst_ni,
+
+ .valid_i (msg_valid),
+ .addr_i (msg_addr),
+ .data_i ('{msg_data}),
+
+ .run_i (run),
+ .rand_valid_i (1'b1),
+ .rand_data_i ('0),
+ .rand_consumed_o (),
+
+ .complete_o (unmasked_complete),
+ .state_o (unmasked_state),
+ .clear_i (clear)
+ );
+
+ logic [Width-1:0] compare_states;
+
+ assign compare_states = unmasked_state[0] ^ (masked_state[0] ^ masked_state[1]);
+
+ // Test with value 0
+ logic [1599:0] data_0 ;
+ always_comb begin
+ data_0 = '0;
+ // SHA3-256 ==> r : 1088
+ data_0[1087] = 1'b 1;
+ data_0[2:0] = 3'b 110;
+ end
+
+ // Data input : SHA3-256
+
+ always_ff @(posedge clk_i or negedge rst_ni) begin
+ if (!rst_ni) begin
+ msg_addr <= '0;
+ end else if (msg_valid) begin
+ msg_addr <= msg_addr + 1'b 1;
+ end else if (run) begin
+ msg_addr <= '0;
+ end
+ end
+
+ assign msg_data = data_0[64*msg_addr+:64];
+
+ logic in_progress;
+ always_ff @(posedge clk_i or negedge rst_ni) begin
+ if (!rst_ni) begin
+ in_progress <= 1'b0;
+ end else if (valid_i) begin
+ in_progress <= 1'b 1;
+ end else if (done_o) begin
+ in_progress <= 1'b 0;
+ end
+ end
+
+ typedef enum logic [2:0] {
+ StIdle,
+ StMsg,
+ StRun,
+ StComplete
+ } st_e;
+
+ st_e st, st_d;
+
+
+ always_ff @(posedge clk_i or negedge rst_ni) begin
+ if (!rst_ni) begin
+ st <= StIdle;
+ end else begin
+ st <= st_d;
+ end
+ end
+
+ always_comb begin
+ st_d = st;
+
+ run = 1'b0;
+ msg_valid = 1'b0;
+ done_o = 1'b0;
+
+ clear = 1'b 0;
+
+ unique case (st)
+ StIdle: begin
+ if (valid_i) begin
+ st_d = StMsg;
+ end
+ end
+
+ StMsg: begin
+ if (msg_addr == 1088/64) begin
+ st_d = StRun;
+
+ run = 1'b1;
+ end else begin
+ st_d = StMsg;
+
+ msg_valid = 1'b1;
+ end
+ end
+
+ StRun: begin
+ if (masked_complete) begin
+ st_d = StComplete;
+ end else begin
+ st_d = StRun;
+ end
+ end
+
+ StComplete: begin
+ st_d = StIdle;
+ clear = 1'b1;
+ done_o = 1'b1;
+ end
+
+ default: begin
+ st_d = StIdle;
+ end
+ endcase
+ end
+
+ //`ASSUME_FPV(DataNotPushWhenProgress_A, msg_valid |-> !in_progress && !valid_i)
+ `ASSUME_FPV(ValidPulse_A, ##1 valid_i |=> !valid_i, clk_i, !rst_ni)
+
+
+ logic [255:0] digest_0;
+ // Big-Endian a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a
+ assign digest_0 = 256'h 4a43_f880_4b0a_d882_fa49_3be4_4dff_80f5_62d6_61a0_5647_c151_66d7_1ebf_f8c6_ffa7;
+ `ASSUME_FPV(FixedRandomValue_A, rand_i == '1)
+ `ASSUME_FPV(FixedRandomValid_A, rand_valid_i == '1)
+ `ASSERT(DigestForData0TestSHA3_256_Masked_A,
+ masked_complete |-> (masked_state[0][255:0] ^ masked_state[1][255:0]) == digest_0,
+ clk_i, !rst_ni)
+ `ASSERT(DigestForData0TestSHA3_256_Unmasked_A,
+ unmasked_complete |-> unmasked_state[0][255:0] == digest_0,
+ clk_i, !rst_ni)
+
+ // After a round is completed or at the beginning, golden value and keccak 2share shall be same
+ `ASSERT(MaskedSameToUnmasked_A, masked_complete |-> compare_states == '0, clk_i, !rst_ni)
+
+endmodule
+