rand和randc

在SystemVerilog中,rand和randc都用于随机化变量,但它们的随机行为和使用场景有本质区别:
rand和randc的区别

rand示例(允许重复)

class A;
  rand bit [1:0] x; // 可能重复:0,1,2,3,0,1...
endclass

每次随机化,x可能是0-3中的任意值,可能连续多次相同。

randc示例(不重复)

class B;
  randc bit [1:0] y; // 0,1,2,3的某种排列
endclass

在4次随机化内,y会依次出现0,1,2,3各一次(顺序随机)
第5次随机化时,重新生成新的排列周期。

constraint实现randc

在SystemVerilog里,randc 的“不重复循环”特性是用一个 周期性排列器(permutation engine) 实现的,而这个引擎在 LRM 里并没有公开。我们只能在 “用户空间” 用 rand + constraint 去模仿它的行为。

以下分享两种方法来实现:

1.洗牌法

思路:
预先申请一块 queue(或数组)存放 [0:N-1];
第一次或每次用完后原地洗牌;
每次随机化只取 queue.pop_front(),用完再洗。

class shuffle_randc #(int N = 8);
  rand int unsigned v;          // 外部看到的值
  int unsigned q[$];            // 内部队列

  // 初始化队列
  function new();
    for (int i=0; i<N; i++) q.push_back(i);
  endfunction

  // 洗牌
  function void shuffle(ref int unsigned q[$]);
    for (int i = q.size()-1; i>0; i--) begin
      int j = $urandom_range(i,0);
      {q[i],q[j]} = {q[j],q[i]};
    end
  endfunction

  // 约束:v 必须等于队列头
  constraint pick {
    q.size() > 0 -> v == q[0];
  }

  // 取完值后把队列头弹掉,空了就再洗
  function void post_randomize();
    void'(q.pop_front());
    if (q.size()==0) begin
      for (int i=0; i<N; i++) q.push_back(i);
      shuffle(q);
    end
  endfunction
endclass

优点:代码短,易理解,真正的均匀排列。
缺点:需要额外存储 N 个元素;若 N 很大会占内存。

2.标记位数组法

思路:
用 bit used[N] 记录哪些值已出现过;
每次随机化时,在未使用的值里随机挑一个;
满一轮后清零 used,重新开始。

class bitmap_randc #(int N = 8);
  rand int unsigned v;
  bit used[N];

  constraint pick_unused {
    !used[v];
  }

  function void post_randomize();
    used[v] = 1;
    if (used.and() with (item)) begin   // 本轮已全用掉
      used = '{default:0};              // 清零
    end
  endfunction
endclass

优点:内存固定为 N bit,与 N 大小无关;
缺点:约束求解器每次要排除已用的值,大 N 时性能下降(稀疏域问题)。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐