//====================================================================== // // tb_chacha_core.v // ----------------- // Testbench for the Chacha stream cipher core. // // // Author: Joachim Strombergson // Copyright (c) 2011, NORDUnet A/S All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // - Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // - Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // - Neither the name of the NORDUnet nor the names of its contributors may // be used to endorse or promote products derived from this software // without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS // IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED // TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A // PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED // TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //====================================================================== module tb_chacha_core(); //---------------------------------------------------------------- // Internal constant and parameter definitions. //---------------------------------------------------------------- parameter CLK_HALF_PERIOD = 2; parameter CLK_PERIOD = 2 * CLK_HALF_PERIOD; parameter TC1 = 1; parameter TC2 = 2; parameter TC3 = 3; parameter TC4 = 4; parameter TC5 = 5; parameter TC6 = 6; parameter TC7 = 7; parameter TC8 = 8; parameter TC9 = 9; parameter TC10 = 10; parameter ONE = 1; parameter TWO = 2; parameter THREE = 3; parameter FOUR = 4; parameter FIVE = 5; parameter SIX = 6; parameter SEVEN = 7; parameter EIGHT = 8; parameter KEY_128_BITS = 0; parameter KEY_256_BITS = 1; parameter EIGHT_ROUNDS = 8; parameter TWELWE_ROUNDS = 12; parameter TWENTY_ROUNDS = 20; parameter DISABLE = 0; parameter ENABLE = 1; localparam DEFAULT_CTR_INIT = 64'h0; //---------------------------------------------------------------- // Register and Wire declarations. //---------------------------------------------------------------- reg [31 : 0] cycle_ctr; reg [31 : 0] error_ctr; reg [31 : 0] tc_ctr; reg tb_clk; reg tb_reset_n; reg tb_core_init; reg tb_core_next; reg [255 : 0] tb_core_key; reg tb_core_keylen; reg [4 : 0] tb_core_rounds; reg [63 : 0] tb_core_iv; wire tb_core_ready; reg [0 : 511] tb_core_data_in; wire [0 : 511] tb_core_data_out; wire tb_core_data_out_valid; reg display_cycle_ctr; reg display_ctrl_and_ctrs; reg display_qround; reg display_state; //---------------------------------------------------------------- // chacha_core device under test. //---------------------------------------------------------------- chacha_core dut( // Clock and reset. .clk(tb_clk), .reset_n(tb_reset_n), // Control. .init(tb_core_init), .next(tb_core_next), // Parameters. .key(tb_core_key), .keylen(tb_core_keylen), .iv(tb_core_iv), .ctr(DEFAULT_CTR_INIT), .rounds(tb_core_rounds), // Data input. .data_in(tb_core_data_in), // Status output. .ready(tb_core_ready), // Data out with valid signal. .data_out(tb_core_data_out), .data_out_valid(tb_core_data_out_valid) ); //---------------------------------------------------------------- // clk_gen // // Clock generator process. //---------------------------------------------------------------- always begin : clk_gen #CLK_HALF_PERIOD tb_clk = !tb_clk; end // clk_gen //-------------------------------------------------------------------- // dut_monitor // // Monitor that displays different types of information // every cycle depending on what flags test cases enable. // // The monitor includes a cycle counter for the testbench. //-------------------------------------------------------------------- always @ (posedge tb_clk) begin : dut_monitor cycle_ctr = cycle_ctr + 1; // Display cycle counter. if (display_cycle_ctr) begin $display("cycle = %08x:", cycle_ctr); $display("-----------------"); end // Display FSM control state and QR, DR counters. if (display_ctrl_and_ctrs) begin $display("Control and status:"); $display("init = 0x%01x, next = 0x%01x", dut.init, dut.next); $display("ready = 0x%01x, valid = 0x%01x", dut.ready, dut.data_out_valid); $display("qr_ctr_reg = %01x, dr_ctr_reg = %01x", dut.qr_ctr_reg, dut.dr_ctr_reg); $display("chacha_ctrl_reg = %01x", dut.chacha_ctrl_reg); $display(""); end // Display the internal state register. if (display_state) begin $display("Round state:"); $display("state0_reg = 0x%08x, state0_new = 0x%08x", dut.state_reg[00], dut.state_new[00]); $display("state1_reg = 0x%08x, state1_new = 0x%08x", dut.state_reg[01], dut.state_new[01]); $display("state2_reg = 0x%08x, state2_new = 0x%08x", dut.state_reg[02], dut.state_new[02]); $display("state3_reg = 0x%08x, state3_new = 0x%08x", dut.state_reg[03], dut.state_new[03]); $display("state4_reg = 0x%08x, state4_new = 0x%08x", dut.state_reg[04], dut.state_new[04]); $display("state5_reg = 0x%08x, state5_new = 0x%08x", dut.state_reg[05], dut.state_new[05]); $display("state6_reg = 0x%08x, state6_new = 0x%08x", dut.state_reg[06], dut.state_new[06]); $display("state7_reg = 0x%08x, state7_new = 0x%08x", dut.state_reg[07], dut.state_new[07]); $display("state8_reg = 0x%08x, state8_new = 0x%08x", dut.state_reg[08], dut.state_new[08]); $display("state9_reg = 0x%08x, state9_new = 0x%08x", dut.state_reg[09], dut.state_new[09]); $display("state10_reg = 0x%08x, state10_new = 0x%08x", dut.state_reg[10], dut.state_new[10]); $display("state11_reg = 0x%08x, state11_new = 0x%08x", dut.state_reg[11], dut.state_new[11]); $display("state12_reg = 0x%08x, state12_new = 0x%08x", dut.state_reg[12], dut.state_new[12]); $display("state13_reg = 0x%08x, state13_new = 0x%08x", dut.state_reg[13], dut.state_new[13]); $display("state14_reg = 0x%08x, state14_new = 0x%08x", dut.state_reg[14], dut.state_new[14]); $display("state15_reg = 0x%08x, state15_new = 0x%08x", dut.state_reg[15], dut.state_new[15]); $display("init_state = 0x%01x, update_state = 0x%01x, state_we = 0x%01x", dut.init_state, dut.update_state, dut.state_we); $display(""); end // Display the qround input and outputs. if (display_qround) begin $display("QR0:"); $display("a = 0x%08x, b = 0x%08x, c = 0x%08x, d = 0x%08x", dut.qr0_a, dut.qr0_b, dut.qr0_c, dut.qr0_d); $display("a_prim = 0x%08x, b_prim = 0x%08x, c_prim = 0x%08x, d_prim = 0x%08x", dut.qr0_a_prim, dut.qr0_b_prim, dut.qr0_c_prim, dut.qr0_d_prim); $display("a0 = 0x%08x, a1 = 0x%08x", dut.qr0.qr.a0, dut.qr0.qr.a1); $display("a0_reg = 0x%08x, a0_new = 0x%08x", dut.qr0.a0_reg, dut.qr0.a0_new); $display(""); end $display(""); end // dut_monitor //---------------------------------------------------------------- // dump_state() // Dump the internal CHACHA state to std out. //---------------------------------------------------------------- task dump_state; begin $display(""); $display("Internal state:"); $display("---------------"); $display("Round state:"); $display("state0_reg = %08x, state1_reg = %08x, state2_reg = %08x, state3_reg = %08x", dut.state_reg[00], dut.state_reg[01], dut.state_reg[02], dut.state_reg[03]); $display("state4_reg = %08x, state5_reg = %08x, state6_reg = %08x, state7_reg = %08x", dut.state_reg[04], dut.state_reg[05], dut.state_reg[06], dut.state_reg[07]); $display("state8_reg = %08x, state9_reg = %08x, state10_reg = %08x, state11_reg = %08x", dut.state_reg[08], dut.state_reg[09], dut.state_reg[10], dut.state_reg[11]); $display("state12_reg = %08x, state13_reg = %08x, state14_reg = %08x, state15_reg = %08x", dut.state_reg[12], dut.state_reg[13], dut.state_reg[14], dut.state_reg[15]); $display(""); $display("rounds = %01x", dut.rounds); $display("qr_ctr_reg = %01x, dr_ctr_reg = %01x", dut.qr_ctr_reg, dut.dr_ctr_reg); $display("block0_ctr_reg = %08x, block1_ctr_reg = %08x", dut.block0_ctr_reg, dut.block1_ctr_reg); $display(""); $display("chacha_ctrl_reg = %02x", dut.chacha_ctrl_reg); $display(""); $display("data_in = %064x", dut.data_in); $display("data_out_valid_reg = %01x", dut.data_out_valid_reg); $display(""); $display("qr0_a_prim = %08x, qr0_b_prim = %08x", dut.qr0_a_prim, dut.qr0_b_prim); $display("qr0_c_prim = %08x, qr0_d_prim = %08x", dut.qr0_c_prim, dut.qr0_d_prim); $display(""); end endtask // dump_state //---------------------------------------------------------------- // dump_inout() // Dump the status for input and output ports. //---------------------------------------------------------------- task dump_inout; begin $display(""); $display("State for input and output ports:"); $display("---------------------------------"); $display("init = %01x", dut.init); $display("next = %01x", dut.next); $display("keylen = %01x", dut.keylen); $display(""); $display("key = %032x", dut.key); $display("iv = %016x", dut.iv); $display(""); $display("ready = %01x", dut.ready); $display("data_in = %064x", dut.data_in); $display("data_out = %064x", dut.data_out); $display("data_out_valid = %01x", dut.data_out_valid); $display(""); end endtask // dump_inout //---------------------------------------------------------------- // test_quarterround() // // Test the quarterround by forcing the inputs of the logic // to known given values and observing the result. //---------------------------------------------------------------- task test_quarterround(input [31 : 0] a, input [31 : 0] b, input [31 : 0] c, input [31 : 0] d); begin $display("Test of quarterround."); $display("a = 0x%08x, b = 0x%08x", a, b); $display("c = 0x%08x, d = 0x%08x", c, d); $display(""); dut.qr0_a = a; dut.qr0_b = b; dut.qr0_c = c; dut.qr0_d = d; #(CLK_PERIOD); $display("a0 = 0x%08x, a1 = 0x%08x", dut.qr0.qr.a0, dut.qr0.qr.a1); $display("b0 = 0x%08x, b1 = 0x%08x", dut.qr0.qr.b0, dut.qr0.qr.b1); $display("b2 = 0x%08x, b3 = 0x%08x", dut.qr0.qr.b2, dut.qr0.qr.b3); $display("c0 = 0x%08x, c1 = 0x%08x", dut.qr0.qr.c0, dut.qr0.qr.c1); $display("d0 = 0x%08x, d1 = 0x%08x", dut.qr0.qr.d0, dut.qr0.qr.d1); $display("d2 = 0x%08x, d3 = 0x%08x", dut.qr0.qr.d2, dut.qr0.qr.d3); $display(""); $display("a_prim = 0x%08x, b_prim = 0x%08x", dut.qr0_a_prim, dut.qr0_b_prim); $display("c_prim = 0x%08x, d_prim = 0x%08x", dut.qr0_c_prim, dut.qr0_d_prim); $display(""); end endtask // test_quarterround //---------------------------------------------------------------- // qr_tests() // // Run some simple test on the qr logic. // Note: Not self testing. No expected value used. //---------------------------------------------------------------- task qr_tests; begin $display("*** Test of Quarterround:"); $display(""); test_quarterround(32'h11223344, 32'h11223344, 32'h11223344, 32'h11223344); test_quarterround(32'h55555555, 32'h55555555, 32'h55555555, 32'h55555555); end endtask // qr_tests //---------------------------------------------------------------- // set_core_init() // // Set core init flag to given value. //---------------------------------------------------------------- task set_core_init(input value); begin tb_core_init = value; end endtask // set_core_init //---------------------------------------------------------------- // set_core_next() // // Set code next flag to given value. //---------------------------------------------------------------- task set_core_next(input value); begin tb_core_next = value; end endtask // set_core_next //---------------------------------------------------------------- // set_core_key_iv_rounds() // // Sets the core key, iv and rounds indata ports // to the given values. //---------------------------------------------------------------- task set_core_key_iv_rounds(input [255 : 0] key, input key_length, input [63 : 0] iv, input [4 : 0] rounds); begin tb_core_key = key; tb_core_keylen = key_length; tb_core_iv = iv; tb_core_rounds = rounds; end endtask // set_core_key_iv //---------------------------------------------------------------- // cycle_reset() // // Cycles the reset signal on the dut. //---------------------------------------------------------------- task cycle_reset; begin tb_reset_n = 0; #(CLK_PERIOD); @(negedge tb_clk) tb_reset_n = 1; #(CLK_PERIOD); end endtask // cycle_reset //---------------------------------------------------------------- // run_test_case // // Runs a test case based on the given key, keylenght, IV and // expected data out from the DUT. //---------------------------------------------------------------- task run_test_case(input [7 : 0] major, input [7 : 0] minor, input [255 : 0] key, input key_length, input [63 : 0] iv, input [4 : 0] rounds, input [511 : 0] expected); begin $display("*** TC %0d-%0d started.", major, minor); $display(""); tc_ctr = tc_ctr + 1; cycle_reset(); set_core_key_iv_rounds(key, key_length, iv, rounds); set_core_init(1); #(CLK_PERIOD); set_core_init(0); dump_state(); // Wait for valid flag and check results. @(posedge tb_core_data_out_valid); dump_state(); if (tb_core_data_out == expected) begin $display("*** TC %0d-%0d successful", major, minor); $display("Got: 0x%064x", tb_core_data_out); $display(""); end else begin $display("*** ERROR: TC %0d-%0d not successful", major, minor); $display("Got: 0x%064x", tb_core_data_out); $display("Expected: 0x%064x", expected); $display(""); error_ctr = error_ctr + 1; end end endtask // run_test_case //---------------------------------------------------------------- // display_test_result() // // Display the accumulated test results. //---------------------------------------------------------------- task display_test_result; begin if (error_ctr == 0) begin $display("*** All test cases completed successfully"); end else begin $display("*** %02d test cases did not complete successfully.", error_ctr); end end endtask // display_test_result //---------------------------------------------------------------- // init_dut() // // Set the input to the DUT to defined values. //---------------------------------------------------------------- task init_dut; begin cycle_ctr = 0; tb_clk = 0; tb_reset_n = 0; error_ctr = 0; tc_ctr = 0; set_core_key_iv_rounds(256'h0000000000000001000000000000000100000000000000010000000000000001, 1'b0, 64'h0000000000000001, 5'b01000); tb_core_init = 0; tb_core_next = 0; tb_core_data_in = 512'h0; end endtask // init_dut //---------------------------------------------------------------- // set_display_prefs() // // Set the different monitor displays we want to see during // simulation. //---------------------------------------------------------------- task set_display_prefs( input cycles, input ctrl_ctr, input state, input x_state, input qround); begin display_cycle_ctr = cycles; display_ctrl_and_ctrs = ctrl_ctr; display_state = state; display_qround = qround; end endtask // set_display_prefs //---------------------------------------------------------------- // chacha_core_test // // The main test functionality. //---------------------------------------------------------------- initial begin : chacha_core_test $display(" -- Testbench for chacha_core started --"); $display(""); set_display_prefs(1, 1, 1, 1, 1); qr_tests(); init_dut(); $display("*** State at init:"); $display(""); dump_state(); #(2 * CLK_PERIOD); @(negedge tb_clk) tb_reset_n = 1; #(CLK_PERIOD); $display("*** State after release of reset:"); $display(""); dump_state(); $display("TC1-1: All zero inputs. 128 bit key, 8 rounds."); run_test_case(TC1, ONE, 256'h0, KEY_128_BITS, 64'h0, EIGHT_ROUNDS, 512'he28a5fa4a67f8c5defed3e6fb7303486aa8427d31419a729572d777953491120b64ab8e72b8deb85cd6aea7cb6089a101824beeb08814a428aab1fa2c816081b); $display("TC1-2: All zero inputs. 128 bit key, 12 rounds."); run_test_case(TC1, TWO, 256'h0, KEY_128_BITS, 64'h0, TWELWE_ROUNDS, 512'he1047ba9476bf8ff312c01b4345a7d8ca5792b0ad467313f1dc412b5fdce32410dea8b68bd774c36a920f092a04d3f95274fbeff97bc8491fcef37f85970b450); $display("TC1-3: All zero inputs. 128 bit key, 20 rounds."); run_test_case(TC1, THREE, 256'h0, KEY_128_BITS, 64'h0, TWENTY_ROUNDS, 512'h89670952608364fd00b2f90936f031c8e756e15dba04b8493d00429259b20f46cc04f111246b6c2ce066be3bfb32d9aa0fddfbc12123d4b9e44f34dca05a103f); $display("TC1-4: All zero inputs. 256 bit key, 8 rounds."); run_test_case(TC1, FOUR, 256'h0, KEY_256_BITS, 64'h0, EIGHT_ROUNDS, 512'h3e00ef2f895f40d67f5bb8e81f09a5a12c840ec3ce9a7f3b181be188ef711a1e984ce172b9216f419f445367456d5619314a42a3da86b001387bfdb80e0cfe42); $display("TC1-5: All zero inputs. 256 bit key, 12 rounds."); run_test_case(TC1, FIVE, 256'h0, KEY_256_BITS, 64'h0, TWELWE_ROUNDS, 512'h9bf49a6a0755f953811fce125f2683d50429c3bb49e074147e0089a52eae155f0564f879d27ae3c02ce82834acfa8c793a629f2ca0de6919610be82f411326be); $display("TC1-6: All zero inputs. 256 bit key, 20 rounds."); run_test_case(TC1, SIX, 256'h0, KEY_256_BITS, 64'h0, TWENTY_ROUNDS, 512'h76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586); $display("TC2-1: One bit in key set, all zero IV. 128 bit key, 8 rounds."); run_test_case(TC2, ONE, 256'h0100000000000000000000000000000000000000000000000000000000000000, KEY_128_BITS, 64'h0, EIGHT_ROUNDS, 512'h03a7669888605a0765e8357475e58673f94fc8161da76c2a3aa2f3caf9fe5449e0fcf38eb882656af83d430d410927d55c972ac4c92ab9da3713e19f761eaa14); $display("TC2-2: One bit in key set, all zero IV. 256 bit key, 8 rounds."); run_test_case(TC2, ONE, 256'h0100000000000000000000000000000000000000000000000000000000000000, KEY_256_BITS, 64'h0, EIGHT_ROUNDS, 512'hcf5ee9a0494aa9613e05d5ed725b804b12f4a465ee635acc3a311de8740489ea289d04f43c7518db56eb4433e498a1238cd8464d3763ddbb9222ee3bd8fae3c8); $display("TC3-1: All zero key, one bit in IV set. 128 bit key, 8 rounds."); run_test_case(TC3, ONE, 256'h0, KEY_128_BITS, 64'h0100000000000000, EIGHT_ROUNDS, 512'h25f5bec6683916ff44bccd12d102e692176663f4cac53e719509ca74b6b2eec85da4236fb29902012adc8f0d86c8187d25cd1c486966930d0204c4ee88a6ab35); $display("TC4-1: All bits in key and IV are set. 128 bit key, 8 rounds."); run_test_case(TC4, ONE, 256'hffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, KEY_128_BITS, 64'hffffffffffffffff, EIGHT_ROUNDS, 512'h2204d5b81ce662193e00966034f91302f14a3fb047f58b6e6ef0d721132304163e0fb640d76ff9c3b9cd99996e6e38fad13f0e31c82244d33abbc1b11e8bf12d); $display("TC5-1: Even bits in key, IV are set. 128 bit key, 8 rounds."); run_test_case(TC5, ONE, 256'h5555555555555555555555555555555555555555555555555555555555555555, KEY_128_BITS, 64'h5555555555555555, EIGHT_ROUNDS, 512'hf0a23bc36270e18ed0691dc384374b9b2c5cb60110a03f56fa48a9fbbad961aa6bab4d892e96261b6f1a0919514ae56f86e066e17c71a4176ac684af1c931996); $display("TC6-1: Odd bits in key, IV are set. 128 bit key, 8 rounds."); run_test_case(TC6, ONE, 256'haaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, KEY_128_BITS, 64'haaaaaaaaaaaaaaaa, EIGHT_ROUNDS, 512'h312d95c0bc38eff4942db2d50bdc500a30641ef7132db1a8ae838b3bea3a7ab03815d7a4cc09dbf5882a3433d743aced48136ebab73299506855c0f5437a36c6); $display("TC7-1: Increasing, decreasing sequences in key and IV. 128 bit key, 8 rounds"); run_test_case(TC7, ONE, 256'h00112233445566778899aabbccddeeff00000000000000000000000000000000, KEY_128_BITS, 64'h0f1e2d3c4b596877, EIGHT_ROUNDS, 512'ha7a6c81bd8ac106e8f3a46a1bc8ec702e95d18c7e0f424519aeafb54471d83a2bf888861586b73d228eaaf82f9665a5a155e867f93731bfbe24fab495590b231); $display("TC7-2: Increasing, decreasing sequences in key and IV. 256 bit key, 8 rounds."); run_test_case(TC7, TWO, 256'h00112233445566778899aabbccddeeffffeeddccbbaa99887766554433221100, KEY_256_BITS, 64'h0f1e2d3c4b596877, EIGHT_ROUNDS, 512'h60fdedbd1a280cb741d0593b6ea0309010acf18e1471f68968f4c9e311dca149b8e027b47c81e0353db013891aa5f68ea3b13dd2f3b8dd0873bf3746e7d6c567); $display("TC8-128-8: Random inputs. 128 bit key, 8 rounds."); run_test_case(TC8, ONE, 256'hc46ec1b18ce8a878725a37e780dfb73500000000000000000000000000000000, KEY_128_BITS, 64'h1ada31d5cf688221, EIGHT_ROUNDS, 512'h6a870108859f679118f3e205e2a56a6826ef5a60a4102ac8d4770059fcb7c7bae02f5ce004a6bfbbea53014dd82107c0aa1c7ce11b7d78f2d50bd3602bbd2594); // Finish in style. $display("*** chacha_core simulation done ***"); display_test_result(); $finish; end // chacha_core_test endmodule // tb_chacha_core //====================================================================== // EOF tb_chacha_core.v //======================================================================