aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/sim/packages/PkgRandom.sv
blob: 94e08d44703a9527f1f96498ffaa04712692f8ce (plain)
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
//
// Copyright 2021 Ettus Research, A National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Package: PkgRandom
//
// Description:
//
//   SystemVerilog has great randomization support, but some features require a
//   more expensive license or aren't supported by all tools. This package
//   tries to fill that gap by providing some useful randomization functions
//   beyond what's supported by standard Verilog.
//

package PkgRandom;

  import PkgMath::*;

  //---------------------------------------------------------------------------
  // Functions
  //---------------------------------------------------------------------------

  // Return a real value in the range [0,max), where max is 1.0 by default.
  function automatic real frand(real max = 1.0);
    bit [63:0] real_bits;
    real num;

    // Build a double-precision floating point value per IEEE-754 standard,
    // which SystemVerilog follows.

    // Positive, with exponent 0
    real_bits[63:52] = 12'h3FF;

    // Mantissa in the range [1.0, 2.0). The leading 1 in the mantissa is
    // implied by the floating point format.
    real_bits[31: 0] = $urandom();
    real_bits[51:32] = $urandom();

    // Compensate for the implied leading 1 in the mantissa by subtracting 1.
    num = $bitstoreal(real_bits) - 1.0;

    // Scale the result to return a value in the desired range.
    return num * max;
  endfunction : frand


  // Return a real value in the range [a,b), [b,a), or [0,a) depending on
  // whether a or b is larger and whether b is provided. This matches the
  // behavior of $urandom_range().
  //
  //   frand_range(1.0, 2.0) -> Random value in the range [1,2)
  //   frand_range(2.0, 1.0) -> Random value in the range [1,2)
  //   frand_range(1.0)      -> Random value in the range [0,1)
  //
  function automatic real frand_range(real a = 1.0, real b = 0.0);
    if (a > b) return b + frand(a - b);
    if (b > a) return a + frand(b - a);
    return a;
  endfunction : frand_range


  // Return a real value with a normal distribution, having the mean value mu
  // and standard deviation sigma.
  function automatic real frandn(real sigma = 1.0, real mu = 0.0);
    // Use the Box-Muller transform to convert uniform random variables to a
    // Gaussian one.
    return sigma*$sqrt(-2.0*$ln(frand())) * $cos(TAU*frand()) + mu;
  endfunction : frandn


  //---------------------------------------------------------------------------
  // Template Functions
  //---------------------------------------------------------------------------

  class Rand #(WIDTH = 64);

    // These are static class functions. They can be called directly, as in:
    //
    //   Rand#(N)::rand_bit()
    //
    // Or, you can declare an object reference, as in:
    //
    //   Rand #(N) rand;
    //   rand.rand_bit();

    typedef bit        [WIDTH-1:0] unsigned_t;
    typedef bit signed [WIDTH-1:0] signed_t;


    // Returns a WIDTH-bit random bit packed array.
    static function unsigned_t rand_bit();
      unsigned_t result;
      int num_rand32 = (WIDTH + 31) / 32;
      for (int i = 0; i < num_rand32; i++) begin
        result = {result, $urandom()};
      end
      return result;
    endfunction : rand_bit


    // Returns a WIDTH-bit random number in the UNSIGNED range [a,b], [b,a], or
    // [0,a] depending on whether a or b is greater and if b is provided. This
    // is equivalent to $urandom_range() but works with any length.
    static function bit [WIDTH-1:0] rand_bit_range(
      unsigned_t a = {WIDTH{1'b1}},
      unsigned_t b = 0
    );
      unsigned_t num;
      int num_bits;
      if (a > b) begin
        // Swap a and b
        unsigned_t temp;
        temp = a;
        a = b;
        b = temp;
      end
      num_bits = $clog2(b - a + unsigned_t'{1});
      do begin
        num = a + (rand_bit() & ((unsigned_t'{1} << num_bits) - 1));
      end while (num > b);
      return num;
    endfunction : rand_bit_range


    // Returns a random number in the given SIGNED range. Behavior is the same
    // as rand_bit_range(), bunsigned_t treats the range values as SIGNED numbers.
    static function signed_t rand_sbit_range(
      signed_t a = {1'b0, {WIDTH{1'b1}}},
      signed_t b = 0
    );
      if (a > b) return b + $signed(rand_bit_range(0, a-b));
      if (b > a) return a + $signed(rand_bit_range(0, b-a));
      return a;
    endfunction : rand_sbit_range


    // Rand#(WIDTH)::rand_logic() returns a WIDTH-bit random logic packed
    // array. Each bit will be 0 or 1 with equal probability (not X or Z).
    static function logic [WIDTH-1:0] rand_logic();
      return rand_bit();
    endfunction : rand_logic

  endclass : Rand

endpackage : PkgRandom