aboutsummaryrefslogblamecommitdiff
path: root/src/tb/tb_chacha.v
blob: fab2ad7647987e80064210a64652c09c51b693e0 (plain) (tree)





































                                                                           




                                                                    
                       
 

                                              





























                                

                     





























                                      

















                                      


























                                                                    
                         





                                                                    

                                  

                                

                                     
                                    




























                                                                        

















                                                                                                                                 



















                                                                                                 
              
                                                                    
                 
         


                        
       
                      


                                                                    


                                                
                                                                    
                
         












                             
       
                     












                                                                    
                    

                        

                            














                                                                    
                    

                        

                            








                                                                    
                      





                                                  



                                                    



                                                                                      

                   
                                                                               

                   







                                                                                                          

                   












                                                                                                 









                                                                    
                       



                                      








                                                                                                               

                   
                                                 







                                                                                                                 
                                                    














                                                                                                 
                           













                                                                                    






                                                                    
                       
























                                                                    
                        

















                                                                    

                                                    












                                                                    
                        

                                         
                        













                                                                    
                        

                                         
                        





                                           
                            














                                                                    
                  














                                                                    
                    





































                                                                    



























                                                                                    

















                                                                    








































































                                                                    
                               
                   

                                  




























                                                                    
                 




                                             


                                

                                                                 
                           
                                 
                          






























                                                                                                                                                                        
//======================================================================
//
// tb_chacha.v
// -----------
// Testbench for the Chacha top level wrapper.
//
//
// 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();

  //----------------------------------------------------------------
  // Internal constant and parameter definitions.
  //----------------------------------------------------------------
  localparam DEBUG = 1;

  localparam CLK_HALF_PERIOD = 1;
  localparam CLK_PERIOD = 2 * CLK_HALF_PERIOD;

  localparam TC1  = 1;
  localparam TC2  = 2;
  localparam TC3  = 3;
  localparam TC4  = 4;
  localparam TC5  = 5;
  localparam TC6  = 6;
  localparam TC7  = 7;
  localparam TC8  = 8;
  localparam TC9  = 9;
  localparam TC10 = 10;

  localparam ONE   = 1;
  localparam TWO   = 2;
  localparam THREE = 3;
  localparam FOUR  = 4;
  localparam FIVE  = 5;
  localparam SIX   = 6;
  localparam SEVEN = 7;
  localparam EIGHT = 8;

  localparam KEY_128_BITS = 0;
  localparam KEY_256_BITS = 1;

  localparam EIGHT_ROUNDS  = 8;
  localparam TWELWE_ROUNDS = 12;
  localparam TWENTY_ROUNDS = 20;

  localparam DISABLE = 0;
  localparam ENABLE  = 1;

  // API for the dut.
  localparam ADDR_NAME0       = 8'h00;
  localparam ADDR_NAME1       = 8'h01;
  localparam ADDR_VERSION     = 8'h02;

  localparam ADDR_CTRL        = 8'h08;
  localparam CTRL_INIT_BIT    = 0;
  localparam CTRL_NEXT_BIT    = 1;

  localparam ADDR_STATUS      = 8'h09;
  localparam STATUS_READY_BIT = 0;

  localparam ADDR_KEYLEN      = 8'h0a;
  localparam KEYLEN_BIT       = 0;
  localparam ADDR_ROUNDS      = 8'h0b;
  localparam ROUNDS_HIGH_BIT  = 4;
  localparam ROUNDS_LOW_BIT   = 0;

  localparam ADDR_KEY0        = 8'h10;
  localparam ADDR_KEY1        = 8'h11;
  localparam ADDR_KEY2        = 8'h12;
  localparam ADDR_KEY3        = 8'h13;
  localparam ADDR_KEY4        = 8'h14;
  localparam ADDR_KEY5        = 8'h15;
  localparam ADDR_KEY6        = 8'h16;
  localparam ADDR_KEY7        = 8'h17;

  localparam ADDR_IV0         = 8'h20;
  localparam ADDR_IV1         = 8'h21;

  localparam ADDR_DATA_IN0    = 8'h40;
  localparam ADDR_DATA_IN15   = 8'h4f;

  localparam ADDR_DATA_OUT0   = 8'h80;
  localparam ADDR_DATA_OUT1   = 8'h81;
  localparam ADDR_DATA_OUT2   = 8'h82;
  localparam ADDR_DATA_OUT3   = 8'h83;
  localparam ADDR_DATA_OUT4   = 8'h84;
  localparam ADDR_DATA_OUT5   = 8'h85;
  localparam ADDR_DATA_OUT6   = 8'h86;
  localparam ADDR_DATA_OUT7   = 8'h87;
  localparam ADDR_DATA_OUT8   = 8'h88;
  localparam ADDR_DATA_OUT9   = 8'h89;
  localparam ADDR_DATA_OUT10  = 8'h8a;
  localparam ADDR_DATA_OUT11  = 8'h8b;
  localparam ADDR_DATA_OUT12  = 8'h8c;
  localparam ADDR_DATA_OUT13  = 8'h8d;
  localparam ADDR_DATA_OUT14  = 8'h8e;
  localparam ADDR_DATA_OUT15  = 8'h8f;


  //----------------------------------------------------------------
  // Register and Wire declarations.
  //----------------------------------------------------------------
  reg tb_clk;
  reg tb_reset_n;

  reg           tb_cs;
  reg           tb_write_read;

  reg  [7 : 0]  tb_address;
  reg  [31 : 0] tb_data_in;
  wire [31 : 0] tb_data_out;
  wire          tb_error;

  reg [63 : 0] cycle_ctr;
  reg [31 : 0] error_ctr;
  reg [31 : 0] tc_ctr;

  reg          error_found;
  reg [31 : 0] read_data;

  reg [511 : 0] extracted_data;

  reg display_cycle_ctr;
  reg display_read_write;
  reg display_core_state;


  //----------------------------------------------------------------
  // Chacha device under test.
  //----------------------------------------------------------------
  chacha dut(
             .clk(tb_clk),
             .reset_n(tb_reset_n),
             .cs(tb_cs),
             .we(tb_write_read),
             .address(tb_address),
             .write_data(tb_data_in),
             .read_data(tb_data_out)
            );


  //----------------------------------------------------------------
  // clk_gen
  //
  // Clock generator process.
  //----------------------------------------------------------------
  always
    begin : clk_gen
      #CLK_HALF_PERIOD tb_clk = !tb_clk;
    end // clk_gen


  //--------------------------------------------------------------------
  // dut_monitor
  //
  // Monitor displaying information every cycle.
  // Includes the cycle counter.
  //--------------------------------------------------------------------
  always @ (posedge tb_clk)
    begin : dut_monitor
      cycle_ctr = cycle_ctr + 1;

      if (display_cycle_ctr)
        begin
          $display("cycle = %016x:", cycle_ctr);
        end

      if (display_core_state)
        begin
          $display("core ctrl: 0x%02x, core_qr_ctr: 0x%02x, core_dr_ctr: 0x%02x, init: 0x%01x, next: 0x%01x, core_ready: 0x%02x",
                   dut.core.chacha_ctrl_reg, dut.core.qr_ctr_reg,
                   dut.core.dr_ctr_reg, dut.core.init,
                   dut.core.next, dut.core.ready_reg);

          $display("state0_reg  = 0x%08x, state1_reg  = 0x%08x, state2_reg  = 0x%08x, state3_reg  = 0x%08x",
                   dut.core.state_reg[00], dut.core.state_reg[01], dut.core.state_reg[02], dut.core.state_reg[03]);
          $display("state4_reg  = 0x%08x, state5_reg  = 0x%08x, state6_reg  = 0x%08x, state7_reg  = 0x%08x",
                   dut.core.state_reg[04], dut.core.state_reg[05], dut.core.state_reg[06], dut.core.state_reg[07]);
          $display("state8_reg  = 0x%08x, state9_reg  = 0x%08x, state10_reg = 0x%08x, state11_reg = 0x%08x",
                   dut.core.state_reg[08], dut.core.state_reg[09], dut.core.state_reg[10], dut.core.state_reg[11]);
          $display("state12_reg = 0x%08x, state13_reg = 0x%08x, state14_reg = 0x%08x, state15_reg = 0x%08x",
                   dut.core.state_reg[12], dut.core.state_reg[13], dut.core.state_reg[14], dut.core.state_reg[15]);
          $display("");
        end

      if (display_read_write)
        begin

          if (dut.cs)
            begin
              if (dut.we)
                begin
                  $display("*** Write acess: addr 0x%02x = 0x%08x", dut.address, dut.write_data);
                end
              else
                begin
                  $display("*** Read acess: addr 0x%02x = 0x%08x", dut.address, dut.read_data);
                end
            end
        end

    end // dut_monitor


  //----------------------------------------------------------------
  // reset_dut
  //----------------------------------------------------------------
  task reset_dut;
    begin
      tb_reset_n = 0;
      #(2 * CLK_PERIOD);
      tb_reset_n = 1;
    end
  endtask // reset_dut


  //----------------------------------------------------------------
  // init_sim()
  //
  // Set the input to the DUT to defined values.
  //----------------------------------------------------------------
  task init_sim;
    begin
      cycle_ctr     = 0;
      error_ctr     = 0;
      tc_ctr        = 0;
      tb_clk        = 0;
      tb_reset_n    = 0;
      tb_cs         = 0;
      tb_write_read = 0;
      tb_address    = 8'h0;
      tb_data_in    = 32'h0;

      display_cycle_ctr  = 0;
      display_read_write = 0;
      display_core_state = 0;
    end
  endtask // init_sim


  //----------------------------------------------------------------
  // read_reg
  //
  // Task that reads and display the value of
  // a register in the dut.
  //----------------------------------------------------------------
  task read_reg(input [7 : 0] addr);
    begin
      tb_cs         = 1;
      tb_write_read = 0;
      tb_address    = addr;
      #(CLK_PERIOD);
      tb_cs         = 0;
      tb_write_read = 0;
      tb_address    = 8'h0;
      tb_data_in    = 32'h0;
    end
  endtask // read_reg


  //----------------------------------------------------------------
  // write_reg
  //
  // Task that writes to a register in the dut.
  //----------------------------------------------------------------
  task write_reg(input [7 : 0] addr, input [31 : 0] data);
    begin
      tb_cs         = 1;
      tb_write_read = 1;
      tb_address    = addr;
      tb_data_in    = data;
      #(CLK_PERIOD);
      tb_cs         = 0;
      tb_write_read = 0;
      tb_address    = 8'h0;
      tb_data_in    = 32'h0;
    end
  endtask // write_reg


  //----------------------------------------------------------------
  // dump_top_state
  //
  // Dump the internal state of the top to std out.
  //----------------------------------------------------------------
  task dump_top_state;
    begin
      $display("");
      $display("Top internal state");
      $display("------------------");
      $display("init_reg   = %01x", dut.init_reg);
      $display("next_reg   = %01x", dut.next_reg);
      $display("keylen_reg = %01x", dut.keylen_reg);
      $display("rounds_reg = %01x", dut.rounds_reg);
      $display("");

      $display("key0_reg = %08x, key1_reg  = %08x, key2_reg = %08x, key3_reg  = %08x",
               dut.key_reg[0], dut.key_reg[1], dut.key_reg[2], dut.key_reg[3]);
      $display("key4_reg = %08x, key5_reg  = %08x, key6_reg = %08x, key7_reg  = %08x",
               dut.key_reg[4], dut.key_reg[5], dut.key_reg[6], dut.key_reg[7]);
      $display("");

      $display("iv0_reg = %08x, iv1_reg = %08x", dut.iv_reg[0], dut.iv_reg[1]);
      $display("");

      $display("data_in0_reg  = %08x, data_in1_reg   = %08x, data_in2_reg  = %08x, data_in3_reg   = %08x",
               dut.data_in_reg[00], dut.data_in_reg[01], dut.data_in_reg[02], dut.data_in_reg[03]);
      $display("data_in4_reg  = %08x, data_in5_reg   = %08x, data_in6_reg  = %08x, data_in7_reg   = %08x",
               dut.data_in_reg[04], dut.data_in_reg[05], dut.data_in_reg[06], dut.data_in_reg[07]);
      $display("data_in8_reg  = %08x, data_in9_reg   = %08x, data_in10_reg = %08x, data_in11_reg  = %08x",
               dut.data_in_reg[08], dut.data_in_reg[09], dut.data_in_reg[10], dut.data_in_reg[11]);
      $display("data_in12_reg = %08x, data_in13_reg  = %08x, data_in14_reg = %08x, data_in15_reg  = %08x",
               dut.data_in_reg[12], dut.data_in_reg[13], dut.data_in_reg[14], dut.data_in_reg[15]);
      $display("");

      $display("ready = 0x%01x, data_out_valid = %01x", dut.core_ready, dut.core_data_out_valid);
      $display("data_out00 = %08x, data_out01 = %08x, data_out02 = %08x, data_out03 = %08x",
               dut.core_data_out[511 : 480], dut.core_data_out[479 : 448],
               dut.core_data_out[447 : 416], dut.core_data_out[415 : 384]);
      $display("data_out04 = %08x, data_out05 = %08x, data_out06 = %08x, data_out07 = %08x",
               dut.core_data_out[383 : 352], dut.core_data_out[351 : 320],
               dut.core_data_out[319 : 288], dut.core_data_out[287 : 256]);
      $display("data_out08 = %08x, data_out09 = %08x, data_out10 = %08x, data_out11 = %08x",
               dut.core_data_out[255 : 224], dut.core_data_out[223 : 192],
               dut.core_data_out[191 : 160], dut.core_data_out[159 : 128]);
      $display("data_out12 = %08x, data_out13 = %08x, data_out14 = %08x, data_out15 = %08x",
               dut.core_data_out[127 :  96], dut.core_data_out[95  :  64],
               dut.core_data_out[63  :  32], dut.core_data_out[31  :   0]);
      $display("");
    end
  endtask // dump_top_state


  //----------------------------------------------------------------
  // dump_core_state
  //
  // Dump the internal state of the core to std out.
  //----------------------------------------------------------------
  task dump_core_state;
    begin
      $display("");
      $display("Core internal state");
      $display("-------------------");
      $display("Round state:");
      $display("state0_reg  = 0x%08x, state1_reg  = 0x%08x, state2_reg  = 0x%08x, state3_reg  = 0x%08x",
               dut.core.state_reg[00], dut.core.state_reg[01], dut.core.state_reg[02], dut.core.state_reg[03]);
      $display("state4_reg  = 0x%08x, state5_reg  = 0x%08x, state6_reg  = 0x%08x, state7_reg  = 0x%08x",
               dut.core.state_reg[04], dut.core.state_reg[05], dut.core.state_reg[06], dut.core.state_reg[07]);
      $display("state8_reg  = 0x%08x, state9_reg  = 0x%08x, state10_reg = 0x%08x, state11_reg = 0x%08x",
               dut.core.state_reg[08], dut.core.state_reg[09], dut.core.state_reg[10], dut.core.state_reg[11]);
      $display("state12_reg = 0x%08x, state13_reg = 0x%08x, state14_reg = 0x%08x, state15_reg = 0x%08x",
               dut.core.state_reg[12], dut.core.state_reg[13], dut.core.state_reg[14], dut.core.state_reg[15]);
      $display("");

      $display("rounds = %01x", dut.core.rounds);
      $display("qr_ctr_reg = %01x, dr_ctr_reg  = %01x", dut.core.qr_ctr_reg, dut.core.dr_ctr_reg);
      $display("block0_ctr_reg = %08x, block1_ctr_reg = %08x", dut.core.block0_ctr_reg, dut.core.block1_ctr_reg);

      $display("");

      $display("chacha_ctrl_reg = %02x", dut.core.chacha_ctrl_reg);
      $display("");

      $display("data_in = %064x", dut.core.data_in);
      $display("data_out_valid_reg = %01x", dut.core.data_out_valid_reg);
      $display("");

      $display("qr0_a_prim = %08x, qr0_b_prim = %08x", dut.core.qr0_a_prim, dut.core.qr0_b_prim);
      $display("qr0_c_prim = %08x, qr0_d_prim = %08x", dut.core.qr0_c_prim, dut.core.qr0_d_prim);
      $display("");
    end
  endtask // dump_core_state


  //----------------------------------------------------------------
  // display_test_result()
  //
  // Display the accumulated test results.
  //----------------------------------------------------------------
  task display_test_result;
    begin
      if (error_ctr == 0)
        begin
          $display("*** All %02d test cases completed successfully", tc_ctr);
        end
      else
        begin
          $display("*** %02d test cases did not complete successfully.", error_ctr);
        end
    end
  endtask // display_test_result


  //----------------------------------------------------------------
  // read_write_test()
  //
  // Simple test case that tries to read and write to the
  // registers in the dut.
  //
  // Note: Currently not self testing. No expected values.
  //----------------------------------------------------------------
  task read_write_test;
    begin
      tc_ctr = tc_ctr + 1;

      write_reg(ADDR_KEY0, 32'h55555555);
      read_reg(ADDR_KEY0);
      write_reg(ADDR_KEY1, 32'haaaaaaaa);
      read_reg(ADDR_KEY1);
      read_reg(ADDR_CTRL);
      read_reg(ADDR_STATUS);
      read_reg(ADDR_KEYLEN);
      read_reg(ADDR_ROUNDS);

      read_reg(ADDR_KEY0);
      read_reg(ADDR_KEY1);
      read_reg(ADDR_KEY2);
      read_reg(ADDR_KEY3);
      read_reg(ADDR_KEY4);
      read_reg(ADDR_KEY5);
      read_reg(ADDR_KEY6);
      read_reg(ADDR_KEY7);
    end
  endtask // read_write_test


  //----------------------------------------------------------------
  // write_localparams()
  //
  // Write key, iv and other parameters to the dut.
  //----------------------------------------------------------------
  task write_parameters(input [256 : 0] key,
                            input           key_length,
                            input [64 : 0]  iv,
                            input [4 : 0]   rounds);
    begin
      write_reg(ADDR_KEY0, key[255 : 224]);
      write_reg(ADDR_KEY1, key[223 : 192]);
      write_reg(ADDR_KEY2, key[191 : 160]);
      write_reg(ADDR_KEY3, key[159 : 128]);
      write_reg(ADDR_KEY4, key[127 :  96]);
      write_reg(ADDR_KEY5, key[95  :  64]);
      write_reg(ADDR_KEY6, key[63  :  32]);
      write_reg(ADDR_KEY7, key[31 :    0]);
      write_reg(ADDR_IV0, iv[63 : 32]);
      write_reg(ADDR_IV1, iv[31 : 0]);
      write_reg(ADDR_KEYLEN, {{31'h0}, key_length});
      write_reg(ADDR_ROUNDS, {{27'h0}, rounds});
    end
  endtask // write_parameters


  //----------------------------------------------------------------
  // start_init_block()
  //
  // Toggle the init signal in the dut to make it start processing
  // the first block available in the data in registers.
  //
  // Note: It is the callers responsibility to call the function
  // when the dut is ready to react on the init signal.
  //----------------------------------------------------------------
  task start_init_block;
    begin
      write_reg(ADDR_CTRL, 32'h00000001);
      #(2 * CLK_PERIOD);
      write_reg(ADDR_CTRL, 32'h00000000);
    end
  endtask // start_init_block


  //----------------------------------------------------------------
  // start_next_block()
  //
  // Toggle the next signal in the dut to make it start processing
  // the next block available in the data in registers.
  //
  // Note: It is the callers responsibility to call the function
  // when the dut is ready to react on the next signal.
  //----------------------------------------------------------------
  task start_next_block;
    begin
      write_reg(ADDR_CTRL, 32'h00000002);
      #(2 * CLK_PERIOD);
      write_reg(ADDR_CTRL, 32'h00000000);

      if (DEBUG)
        begin
          $display("Debug of next state.");
          dump_core_state();
          #(2 * CLK_PERIOD);
          dump_core_state();
        end
    end
  endtask // start_next_block


  //----------------------------------------------------------------
  // wait_ready()
  //
  // Wait for the ready flag in the dut to be set.
  //
  // Note: It is the callers responsibility to call the function
  // when the dut is actively processing and will in fact at some
  // point set the flag.
  //----------------------------------------------------------------
  task wait_ready;
    begin
      while (!tb_data_out[STATUS_READY_BIT])
        begin
          read_reg(ADDR_STATUS);
        end
    end
  endtask // wait_ready


  //----------------------------------------------------------------
  // extract_data()
  //
  // Extracts all 16 data out words and combine them into the
  // global extracted_data.
  //----------------------------------------------------------------
  task extract_data;
    begin
      read_reg(ADDR_DATA_OUT0);
      extracted_data[511 : 480] = tb_data_out;
      read_reg(ADDR_DATA_OUT1);
      extracted_data[479 : 448] = tb_data_out;
      read_reg(ADDR_DATA_OUT2);
      extracted_data[447 : 416] = tb_data_out;
      read_reg(ADDR_DATA_OUT3);
      extracted_data[415 : 384] = tb_data_out;
      read_reg(ADDR_DATA_OUT4);
      extracted_data[383 : 352] = tb_data_out;
      read_reg(ADDR_DATA_OUT5);
      extracted_data[351 : 320] = tb_data_out;
      read_reg(ADDR_DATA_OUT6);
      extracted_data[319 : 288] = tb_data_out;
      read_reg(ADDR_DATA_OUT7);
      extracted_data[287 : 256] = tb_data_out;
      read_reg(ADDR_DATA_OUT8);
      extracted_data[255 : 224] = tb_data_out;
      read_reg(ADDR_DATA_OUT9);
      extracted_data[223 : 192] = tb_data_out;
      read_reg(ADDR_DATA_OUT10);
      extracted_data[191 : 160] = tb_data_out;
      read_reg(ADDR_DATA_OUT11);
      extracted_data[159 : 128] = tb_data_out;
      read_reg(ADDR_DATA_OUT12);
      extracted_data[127 :  96] = tb_data_out;
      read_reg(ADDR_DATA_OUT13);
      extracted_data[95  :  64] = tb_data_out;
      read_reg(ADDR_DATA_OUT14);
      extracted_data[63  :  32] = tb_data_out;
      read_reg(ADDR_DATA_OUT15);
      extracted_data[31  :   0] = tb_data_out;
    end
  endtask // extract_data


  //----------------------------------------------------------------
  // check_name_version()
  //
  // Read the name and version from the DUT.
  //----------------------------------------------------------------
  task check_name_version;
    reg [31 : 0] name0;
    reg [31 : 0] name1;
    reg [31 : 0] version;
    begin
      $display("*** Trying to read name and version from core.");
      read_reg(ADDR_NAME0);
      name0 = tb_data_out;
      read_reg(ADDR_NAME1);
      name1 = tb_data_out;
      read_reg(ADDR_VERSION);
      version = tb_data_out;

      $display("DUT name: %c%c%c%c%c%c%c%c",
               name0[31 : 24], name0[23 : 16], name0[15 : 8], name0[7 : 0],
               name1[31 : 24], name1[23 : 16], name1[15 : 8], name1[7 : 0]);
      $display("DUT version: %c%c%c%c",
               version[31 : 24], version[23 : 16], version[15 : 8], version[7 : 0]);
      $display("");
    end
  endtask // check_name_version


  //----------------------------------------------------------------
  // run_two_blocks_test_vector()
  //
  // Runs a test case with two blocks based on the given
  // test vector. Only the final block is compared.
  //----------------------------------------------------------------
  task run_two_blocks_test_vector(input [7 : 0]   major,
                                  input [7 : 0]   minor,
                                  input [256 : 0] key,
                                  input           key_length,
                                  input [64 : 0]  iv,
                                  input [4 : 0]   rounds,
                                  input [511 : 0] expected);
    begin
      tc_ctr = tc_ctr + 1;

      $display("***TC%2d-%2d started", major, minor);
      $display("***-----------------");
      write_parameters(key, key_length, iv, rounds);
      start_init_block();
      wait_ready();
      extract_data();

      if (DEBUG)
        begin
          $display("State after first block:");
          dump_core_state();

          $display("First block:");
          $display("0x%064x", extracted_data);
        end

      start_next_block();

      if (DEBUG)
        begin
          $display("State after init of second block:");
          dump_core_state();
        end

      wait_ready();
      extract_data();

      if (DEBUG)
        begin
          $display("State after init of second block:");
          dump_core_state();

          $display("Second block:");
          $display("0x%064x", extracted_data);
        end

      if (extracted_data != expected)
        begin
          error_ctr = error_ctr + 1;
          $display("***TC%2d-%2d - ERROR", major, minor);
          $display("***-----------------");
          $display("Expected:");
          $display("0x%064x", expected);
          $display("Got:");
          $display("0x%064x", extracted_data);
        end
      else
        begin
          $display("***TC%2d-%2d - SUCCESS", major, minor);
          $display("***-------------------");
        end
      $display("");
    end
  endtask // run_two_blocks_test_vector


  //----------------------------------------------------------------
  // run_test_vector()
  //
  // Runs a test case based on the given test vector.
  //----------------------------------------------------------------
  task run_test_vector(input [7 : 0]   major,
                       input [7 : 0]   minor,
                       input [256 : 0] key,
                       input           key_length,
                       input [64 : 0]  iv,
                       input [4 : 0]   rounds,
                       input [511 : 0] expected);
    begin
      tc_ctr = tc_ctr + 1;

      $display("***TC%2d-%2d started", major, minor);
      $display("***-----------------");
      write_parameters(key, key_length, iv, rounds);

      start_init_block();
      $display("*** Started.");
      wait_ready();
      $display("*** Ready seen.");
      dump_top_state();
      extract_data();

      if (extracted_data != expected)
        begin
          error_ctr = error_ctr + 1;
          $display("***TC%2d-%2d - ERROR", major, minor);
          $display("***-----------------");
          $display("Expected:");
          $display("0x%064x", expected);
          $display("Got:");
          $display("0x%064x", extracted_data);
        end
      else
        begin
          $display("***TC%2d-%2d - SUCCESS", major, minor);
          $display("***-------------------");
        end
      $display("");
    end
  endtask // run_test_vector


  //----------------------------------------------------------------
  // chacha_test
  // The main test functionality.
  //----------------------------------------------------------------
  initial
    begin : chacha_test
      $display("   -- Testbench for chacha started --");
      init_sim();
      reset_dut();

      $display("State at init after reset:");
      dump_top_state();

      // Check name and version.
      check_name_version();

      $display("TC1-1: All zero inputs. 128 bit key, 8 rounds.");
      run_test_vector(TC1, ONE,
                    256'h0,
                    KEY_128_BITS,
                    64'h0,
                    EIGHT_ROUNDS,
                    512'he28a5fa4a67f8c5defed3e6fb7303486aa8427d31419a729572d777953491120b64ab8e72b8deb85cd6aea7cb6089a101824beeb08814a428aab1fa2c816081b);

      $display("TC7-2: Increasing, decreasing sequences in key and IV. 256 bit key, 8 rounds.");
      run_test_vector(TC7, TWO,
                    256'h00112233445566778899aabbccddeeffffeeddccbbaa99887766554433221100,
                    KEY_256_BITS,
                    64'h0f1e2d3c4b596877,
                    EIGHT_ROUNDS,
                    512'h60fdedbd1a280cb741d0593b6ea0309010acf18e1471f68968f4c9e311dca149b8e027b47c81e0353db013891aa5f68ea3b13dd2f3b8dd0873bf3746e7d6c567);


      $display("TC7-3: Increasing, decreasing sequences in key and IV. 256 bit key, 8 rounds.");
      $display("TC7-3: Testing correct second block.");
      run_two_blocks_test_vector(TC7, THREE,
                                 256'h00112233445566778899aabbccddeeffffeeddccbbaa99887766554433221100,
                                 KEY_256_BITS,
                                 64'h0f1e2d3c4b596877,
                                 EIGHT_ROUNDS,
                                 512'hfe882395601ce8aded444867fe62ed8741420002e5d28bb573113a418c1f4008e954c188f38ec4f26bb8555e2b7c92bf4380e2ea9e553187fdd42821794416de);


      display_test_result();
      $display("*** chacha simulation done.");
      $finish;
    end // chacha_test
endmodule // tb_chacha

//======================================================================
// EOF tb_chacha.v
//======================================================================