sub_per_hash.sv 7.19 KB
Newer Older
sakundu committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
// Copyright (c) 2019 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License.  You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.

// Author: Wolfgang Roenninger <wroennin@ethz.ch>

// This module implements a fully parameterizable substitution-permutation hash
// function. The hash is structured in stages consisting of a shuffle of the input bits
// and then xoring for each bit 3 pseudo-random bits of the shuffeled vector.
// The hash function is NOT cryptographically secure!
// From the keys it computes a sequence of pseudo-random numbers, which determine the permutations
// and substitutions. As pseudo random generator a multiplicative linear congruential
// generator is used and uses different constants for the computation of the permutation
// and substitution respectively.
// The permutation shuffles the bits using a variant of the Fisher-Yates shuffle algorithm.
// The substitution per stage is the xor of 3 pseudo random bits of the previous stage.
// As shifting and xoring of a signal do not change its distribution, the distribution
// of the output hash is the same as the one of the input data.
//
// Parameters:
// - `InpWidth`:   The input width of the vector `data_i`.
// - `HashWidth`:  The output width of the substitution-permutation hash.
// - `NoRounds`:   The amount of permutation, substitution stages generated. Translates
//                 into how many levels of xor's there will be before optimization.
// - `PermuteKey`: The Key for the pseudo-random generator used for determining the exact
//                 permutation (shuffled wiring between each xor stage) at compile/elaboration.
//                 Any `int unsigned` value can be used as key, however one should examine the
//                 output of the hash function.
// - `XorKey`:     The Key for the pseudo-random generator used for determining the xor
//                 of bits between stages. The same principles as for `PermuteKey` applies,
//                 however one should look that both keys have a greatest common divisor of 1.

module sub_per_hash #(
  parameter int unsigned InpWidth   = 32'd11,
  parameter int unsigned HashWidth  = 32'd5,
  parameter int unsigned NoRounds   = 32'd1,
  parameter int unsigned PermuteKey = 32'd299034753,
  parameter int unsigned XorKey     = 32'd4094834
) (
  // is purely combinational
  input  logic [InpWidth-1:0]     data_i,
  output logic [HashWidth-1:0]    hash_o,
  output logic [2**HashWidth-1:0] hash_onehot_o
);

  // typedefs and respective localparams
  typedef int unsigned perm_lists_t [NoRounds][InpWidth];
  localparam perm_lists_t PERMUTATIONS = get_permutations(PermuteKey);
  // encoding for inner most array:
  // position 0 indicates the number of inputs, 2 or 3
  // the other positions 1 - 3 indicate the inputs
  typedef int unsigned xor_stages_t [NoRounds][InpWidth][3];
  localparam xor_stages_t XorStages = get_xor_stages(XorKey);

  // stage signals
  logic [NoRounds-1:0][InpWidth-1:0] permuted, xored;

  // for each round
  for (genvar r = 0; r < NoRounds; r++) begin : gen_round
    // for each bit
    for (genvar i = 0; i < InpWidth ; i++) begin : gen_sub_per

      // assign the permutation
      if (r == 0) begin : gen_input
        assign permuted[r][i] = data_i[PERMUTATIONS[r][i]];
      end else begin : gen_permutation
        assign permuted[r][i] = permuted[r-1][PERMUTATIONS[r][i]];
      end

      // assign the xor substitution
      assign xored[r][i] = permuted[r][XorStages[r][i][0]] ^
                           permuted[r][XorStages[r][i][1]] ^
                           permuted[r][XorStages[r][i][2]];
    end
  end

  // output assignment, take the bottom bits of the last round
  assign hash_o = xored[NoRounds-1][HashWidth-1:0];
  // for onehot run trough a decoder
  assign hash_onehot_o = 1 << hash_o;

  // PRG is MLCG (multiplicative linear congruential generator)
  // Constant values the same as RtlUniform from Native API
  // X(n+1) = (a*X(n)+c) mod m
  // a: large prime
  // c: increment
  // m: range
  // Shuffling is a variation of the Fisher-Yates shuffle algorithm
  function automatic perm_lists_t get_permutations(input int unsigned seed);
    perm_lists_t indices;
    perm_lists_t perm_array;
    longint unsigned A = 2147483629;
    longint unsigned C = 2147483587;
    longint unsigned M = 2**31 - 1;
    longint unsigned index   = 0;
    longint unsigned advance = 0;
    longint unsigned rand_number = (A * seed + C) % M;

    // do it for each round
    for (int unsigned r = 0; r < NoRounds; r++) begin
      // initialize the index array
      for (int unsigned i = 0; i < InpWidth; i++) begin
        indices[r][i] = i;
      end
      // do the shuffling
      for (int unsigned i = 0; i < InpWidth; i++) begin
        // get the 'random' number
        if (i > 0) begin
          rand_number = (A * rand_number + C) % M;
          index = rand_number % i;
        end
        // do the shuffling
        if (i != index) begin
          perm_array[r][i]     = perm_array[r][index];
          perm_array[r][index] = indices[r][i];
        end
      end
      // advance the PRG a bit
      rand_number = (A * rand_number + C) % M;
      advance     = rand_number % NoRounds;
      for (int unsigned i = 0; i < advance; i++) begin
        rand_number = (A * rand_number + C) % M;
      end
    end
    return perm_array;
  endfunction : get_permutations

  // PRG is MLCG (multiplicative linear congruential generator)
  // Constant values the same as Numerical Recipes
  // X(n+1) = (a*X(n)+c) mod m
  // a: large prime
  // c: increment
  // m: range
  function automatic xor_stages_t get_xor_stages(input int unsigned seed);
    xor_stages_t xor_array;
    longint unsigned A = 1664525;
    longint unsigned C = 1013904223;
    longint unsigned M = 2**32;
    longint unsigned index   = 0;
    // int unsigned even    = 0;
    longint unsigned advance = 0;
    longint unsigned rand_number = (A * seed + C) % M;

    // fill the array with 'randon' inputs
    // for each xor, a even random number is two input, uneven is tree
    // for each round
    for (int unsigned r = 0; r < NoRounds; r++) begin
      // for each bit
      for (int unsigned i = 0; i < InpWidth; i++) begin
        rand_number = (A * rand_number + C) % M;
        // even = rand_number[3];
        for (int unsigned j = 0; j < 3; j++) begin
          rand_number = (A * rand_number + C) % M;
          index = rand_number % InpWidth;
          xor_array[r][i][j] = index;
        end
      end
      // advance the PRG a bit
      rand_number = (A * rand_number + C) % M;
      advance     = rand_number % NoRounds;
      for (int unsigned i = 0; i < advance; i++) begin
        rand_number = (A * rand_number + C) % M;
      end
    end
    return xor_array;
  endfunction : get_xor_stages
endmodule