【芯片验证随心记】1.SystemVerilog中,rand和randc的区别?如何用constraint实现randc?
在SystemVerilog里,randc 的“不重复循环”特性是用一个 周期性排列器(permutation engine) 实现的,而这个引擎在 LRM 里并没有公开。我们只能在 “用户空间” 用 rand + constraint 去模仿它的行为。每次随机化只取 queue.pop_front(),用完再洗。在4次随机化内,y会依次出现0,1,2,3各一次(顺序随机)每次随机化,x可能是0-
rand和randc
在SystemVerilog中,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 时性能下降(稀疏域问题)。
更多推荐
所有评论(0)