赞
踩
随机数生成器(RNG)是SystemVerilog语言及其各种验证方法中的重要组成部分。我们深入研究了线程和对象如何生成随机数,代码中对象和线程的排序如何影响随机化,以及在开发测试平台时需要注意的事项。
作为基于约束的随机验证方法的一部分,在ASIC/FPGA开发过程中,每晚会运行一套测试,并且每晚的回归测试都会使用不同的种子运行。这样,每当测试使用不同的随机种子运行时,测试平台中的 $urandom, $urandom_range & (class) randomize()调用会生成不同的随机数,从而导致DUT的不同激励和配置。
这些回归测试中的任何测试失败都必须是可复现的,特别是如果失败结果证明是RTL错误。然后的处理流程是修复RTL并使用揭示错误的相同随机种子重新运行失败的测试,以验证是否修复了该错误。因此,您应该关注随机稳定性,因为为了成功重现那个失败的测试并验证RTL错误是否已修复,您需要理解随机稳定性和一个重要的属性,称为分层种子(Hierarchical Seeding)。
理解随机稳定性是实现基于约束的随机验证策略成功的关键。
在上文中,我们已经确切地确定了重现测试失败的重要性。现在,为了做到这一点,测试平台必须展现可预测的RNG(随机数生成器)行为,即在程序块、线程和对象中生成的随机数序列应该对于给定的种子而言每次都是相同的。
随机稳定性基本上解释了在SystemVerilog中如何生成随机数以确保可重现性。它由4个属性描述:
要理解对象稳定性和线程稳定性,我们首先需要了解一个关键概念。
在SystemVerilog中,一切都是 “进程” (Process) – 程序块 (program-block)、线程(thread)、对象(object)、函数(function)或任务(task) 调用都是独立的进程,并且SystemVerilog进程的一个重要属性是它们每个都有独立的 RNG(随机数生成器)。
上面的句子让您有点头痛吗?别担心!这里有很多内容…
下面为 示例1.1
及其相应的 CONSOLE OUTPUT
。特别要注意类型为 process 的变量 pp 和 pt。
/* Example 1.1 */ program automatic test; class pkt; function int unsigned getrand(); process local_proc; // Get handle to function's process local_proc = process::self(); $display("class-function randstate %s", local_proc.get_randstate()); endfunction endclass: pkt initial begin int unsigned var_a; pkt p; process pp, pt; string pp_randstate, pc_randstate, pt_randstate; // Get handle to program-block's process pp = process::self(); // Get and print program-block's RNG's randstate pp_randstate = pp.get_randstate(); $display("program-block randstate %s", pp_randstate); fork begin // Get handle to thread's process pt = process::self(); // Get thread's internal RNG randstate pt_randstate = pt.get_randstate(); $display("thread-block randstate %s", pt_randstate); end join p = new(); // Classes have an in-built process defined. // .. You can get object's internal RNG's // .. randstate by calling obj.get_randstate. pc_randstate = p.get_randstate(); $display("class-object randstate %s", pc_randstate); // This function call is a process in itself. // .. Within this function we can fetch the // .. process handle and print randstate p.getrand(); end endprogram
CONSOLE OUTPUT - Mentor Questa Sim
# program-block randstate MSe2aab990e149a42b970a1cd680bde03f
# thread-block randstate MS438ef57f498295b9ba42179530d3415c
# class-object randstate MS9d943356938f2b6cf29ab3e32f1fd461
# class-function randstate MSd3b01445af5da33c46529df6a5d82faa
CONSOLE OUTPUT - Synopsys VCS
program-block randstate 0000000000000000000000000000000000111010101110111011000011010010
thread-block randstate 0000000000000000000000000000000001011100110000001011100011110010
class-object randstate 00Z1ZZZ1Z0ZZ11XZX11Z1ZX1XZ1Z0X01XZZZZZXZXZXZZZZXXXXZZXZZXXZZXXZZ
class-function randstate 0Z110X0X0Z0XZ1XZZ111ZZ1ZZ11Z1XXZZXXXZXZZXXXZZZXZXXZZZZZZXZXXXZZZ
这是我们看到的内容:
这个示例的主要要点是线程和对象有它们自己的RNG。这使得对象和线程表现出确定性和稳定的行为,因为一个线程或对象内的$urandom和randomize()调用不会影响另一个线程或对象。如果有10个线程被fork出来,线程执行的顺序不会影响随机数的生成。
这种行为,其中一个对象实例中的randomize() 方法与其他实例中的 randomize() 调用是独立的,称为对象稳定性。类似地,线程稳定性是指一个线程内的 $urandom()、$urandom_range() 或 std::randomize() 调用不会影响另一个线程,因此线程执行的顺序不会影响随机数的生成。
线程和对象的RNG(即每个进程)是分层种子的。当创建一个线程或new一个对象时,其内部RNG的随机状态将使用父块的RNG的下一个随机值作为种子进行初始化。让我们看一个例子。
示例 2.1
显示在new对象p之前生成了5个随机数。 示例 2.2
显示了在new对象之后生成了相同的5个随机数的for循环。观察控制台输出-你看到了一个模式吗?在示例 2.2
中,第一个随机数(0x1c211259)被用作对象p的内部RNG的种子,之后的for循环继续产生相同的数列。这就是分层种子的作用。
/* Example 2.1 */ program automatic test; class pkt; rand logic [31:0] saddr, daddr, etype; endclass: pkt initial begin pkt p, p_th; int unsigned var_a, var_b, var_c; for (int ii=0; ii<5; ii++) begin var_a = $urandom(); $display("urandom before object new 0x%x", var_a); end p = new(); // Object new after for-loop end endprogram /* Example 2.2 */ program automatic test; class pkt; rand logic [31:0] saddr, daddr, etype; endclass: pkt initial begin pkt p, p_th; int unsigned var_a, var_b, var_c; p = new(); // Object new before for-loop for (int ii=0; ii<5; ii++) begin var_a = $urandom(); $display("urandom before object new 0x%x", var_a); end end endprogram
CONSOLE OUTPUT
/* Example 2.1 */
# urandom before object new 0x1c211259
# urandom before object new 0x762e4948
# urandom before object new 0x3e10f810
# urandom before object new 0x5fd6b9d8
# urandom before object new 0x1bfad73e
/* Example 2.2 */
# urandom after object new 0x762e4948
# urandom after object new 0x3e10f810
# urandom after object new 0x5fd6b9d8
# urandom after object new 0x1bfad73e
# urandom after object new 0xdae9cf76
让我们来看一个带有线程的例子。与 示例 2.1
的输出相比, 示例 2.3
中的第一个随机数由程序块的RNG(0x1c211259)用作对象p的RNG的种子,第二个随机数(0x762e4948)用作线程的内部RNG的种子。在线程加入后,程序块的for循环继续按序列产生下一个随机数(0x3e10f810)。同时,注意线程内部的for循环不会影响线程外部的程序块的for循环。这就是随机稳定性的作用。
/* Example 2.3 */ program automatic test; class pkt; rand logic [31:0] saddr, daddr, etype; endclass: pkt initial begin pkt p, p_th; int unsigned var_a, var_b, var_c; // 1. object new p = new(); p.randomize(); $display("p->saddr 0x%x daddr 0x%x etype 0x%x", p.saddr, p.daddr, p.etype); // 2. fork thread fork begin for (int ii=0; ii<5; ii++) begin var_b = $urandom(); $display("thread-block urandom 0x%x", var_b); end end join // 3. program for-loop for (int ii=0; ii<5; ii++) begin var_a = $urandom(); $display("program-block urandom 0x%x", var_a); end end endprogram
CONSOLE OUTPUT
# p->saddr 0x1ff13f40 daddr 0xd87f903a etype 0xd2b1ebf1
#
# thread-block urandom 0x294f77ba
# thread-block urandom 0x60323223
# thread-block urandom 0x4279ce38
# thread-block urandom 0xb50c1a80
# thread-block urandom 0x6bc779b7
#
# program-block urandom 0x3e10f810
# program-block urandom 0x5fd6b9d8
# program-block urandom 0x1bfad73e
# program-block urandom 0xdae9cf76
# program-block urandom 0xdcf9d962
现在我们了解了分层种子,我们可以得出以下结论:
下一个显而易见的问题是,如果线程和对象由其父级种子,那么程序块是由谁种子的呢?程序块具有称为初始化RNG的东西。您可以将其视为"根RNG"。这些由Synopsys VCS的命令行参数+ntb_random_seed和 Mentor Graphics Questasim 的 -sv_seed进行种子。
除了程序块之外,每个模块实例、接口实例和包实例都有一个初始化RNG。
为了确保您已经理解了这个概念-随机稳定性的4个属性可以与以下火车类比进行比较。如果将随机数生成器(RNG)看作是一辆火车,它生成的随机数序列看作是它的轨道,那么:
现在我们了解了SV中的所有内容都是进程,每个进程都有一个内部RNG,我们可以玩弄这个RNG的随机状态。
有两种方法可以为这些进程的内部RNG提供种子:
示例 4.1
应该是不言自明的。您可以在线程内部使用process::srandom(seed)重新为对象或线程的RNG提供种子,或者对于对象调用obj.srandom(seed)。
/* Example 4.1 */ program automatic test; class pkt; rand int randvar; endclass:pkt initial begin int unsigned var_a; process pp, pt, pc; string pp_randstate, pt_randstate, pc_randstate; pkt p; fork begin pt = process::self(); $display("thread-block randstate %s", pt.get_randstate()); pt.srandom(1234); $display("Changing thread-block randstate %s", pt.get_randstate()); for (int ii=0; ii<5; ii++) begin var_a = $urandom(); $display("var_a %d", var_a); end end join p = new(); $display("object's randstate %s", p.get_randstate()); pt.srandom(6666); $display("Changing object's randstate %s", p.get_randstate()); for(int ii=0; ii<5; ii++) begin p.randomize(); $display("p: p.randvar %d", p.randvar); end end endprogram
CONSOLE OUTPUT:
# thread-block randstate MS438ef57f498295b9ba42179530d3415c
# Changing thread-block randstate MS72dfdf2597f7d0779f2e5602c960a1e5
# var_a 3151364325
# var_a 797357057
# var_a 1347950301
# var_a 4120409912
# var_a 2561059293
# object's randstate MS9d943356938f2b6cf29ab3e32f1fd461
# Changing object's randstate MS9d943356938f2b6cf29ab3e32f1fd461
# p: p.randvar 1042171828
# p: p.randvar 438788930
# p: p.randvar 1389555781
# p: p.randvar 1120102834
# p: p.randvar 820092897
set_randstate()
这是额外的奖励。
在示例 1.1
中,我们看到了如何使用get_randstate()获取进程的RNG的randstate。您还可以使用 set_randstate() 设置进程的RNG 的 randstate。
在示例 3.1
中,我们保持简单-首先在程序块内生成5个随机数,然后在线程内生成5个随机数。这给我们了10个不同的随机数。
现在让我们玩弄一下线程的RNG,并使用 set_randstate() 改变它的状态。在示例3.2中,我们在一开始记录了程序块的RNG randstate,然后在生成5个数字之前将线程的RNG设置为该状态。看看会发生什么-线程生成的随机数与程序块相同!
这基本上确认了通过randomize方法调用($urandom、$urandom_range、randomize)返回的随机数仅取决于该时刻进程的随机数生成器的状态。
/* Example 3.1 */ program automatic test; initial begin int unsigned var_a; process pp, pt, pc; string pp_randstate, pt_randstate, pc_randstate; // Record the state of the program block's RNG right // at the beginning of the initial block pp = process::self(); pp_randstate = pp.get_randstate(); $display("program-block randstate %s", pp_randstate); // Generate 5 random numbers using the program-block's // Random Number Generator (RNG) for (int ii=0; ii<5; ii++) begin var_a = $urandom(); $display("program-block var_a %d", var_a); end fork begin // Since 5 random numbers were generated using // the program-block's RNG, // this thread's RNG state gets initialized by the // 6th random number of the program-block's RNG pt = process::self(); pt_randstate = pt.get_randstate(); $display("thread-block randstate %s", pt_randstate); for (int ii=0; ii<5; ii++) begin var_a = $urandom(); $display("thread var_a %d", var_a); end end join end endprogram
/* Example 3.2 */ program automatic test; class pkt; rand int randvar; endclass:pkt initial begin int unsigned var_a; process pp, pt, pc; string pp_randstate, pt_randstate, pc_randstate; pkt p; // Record the state of the program block's RNG // at the beginning of the initial block pp = process::self(); pp_randstate = pp.get_randstate(); $display("program-block randstate %s", pp_randstate); // Generate 5 random numbers using the program-block's // Random Number Generator (RNG) for (int ii=0; ii<5; ii++) begin var_a = $urandom(); $display("program-block var_a %d", var_a); end fork begin // This thread's RNG state gets initialized by the // 6th random number of the program-block's RNG, // but we over-write this RNG's state to // "pp_randstate" which was the program-block's // state at the start of execution. // Observe the output the 5 random numbers generated // below should be the same as the above for-loop. pt = process::self(); pt.set_randstate(pp_randstate); pt_randstate = pt.get_randstate(); $display("thread-block randstate %s", pt_randstate); for (int ii=0; ii<5; ii++) begin var_a = $urandom(); $display("thread var_a %d", var_a); end end join end endprogram
/* Example 3.1 Output */ # program-block randstate MS0d46fe96c77f6e86d3ff4d7c45fa86ee # program-block var_a 750503525 # program-block var_a 354101471 # program-block var_a 1016169009 # program-block var_a 1500937797 # program-block var_a 3106080629 # thread-block randstate MSb0eb5ff259752d3100c01bb5dd17e861 # thread var_a 1381377367 # thread var_a 335209292 # thread var_a 4170423523 # thread var_a 626467677 # thread var_a 1536526296 /* Example 3.2 Output */ # program-block randstate MS0d46fe96c77f6e86d3ff4d7c45fa86ee # program-block var_a 750503525 # program-block var_a 354101471 # program-block var_a 1016169009 # program-block var_a 1500937797 # program-block var_a 3106080629 # thread-block randstate MS0d46fe96c77f6e86d3ff4d7c45fa86ee # thread var_a 750503525 # thread var_a 354101471 # thread var_a 1016169009 # thread var_a 1500937797 # thread var_a 3106080629
随着这一点,我们已经基本上涵盖了关于随机稳定性的所有讨论内容。
以上内容均来自下面的网址,收获颇丰,特此翻译分享给大家,https://www.systemverilog.io/verification/random-stability/
而且推荐给大家这个网站,是一个很好的学习ASIC/SoC设计和验证的网站 https://www.systemverilog.io/
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。