SV 6. 随机化

文章详细介绍了受约束的随机测试(CRT)技术在系统Verilog(SV)中的应用,包括需要随机化的方面,如器件配置、环境配置、输入数据等。文中讨论了SV中的随机化方法,如使用随机变量、检查randomize()结果、约束求解以及约束的定义和使用。还提到了概率分布控制、错误避免策略和产生原子激励与场景的方法,强调了测试平台的灵活性和可扩展性。
摘要由CSDN通过智能技术生成

文章目录


前言

随着设计变得越来越大,要产生一个完整得激励集来测试设计的功能变得很困难。定向测试集,功能项之间得关系使大多数错误的来源。
采用受约束的随机测试法(CRT)自动产生测试集
产生有效的激励,测试感兴趣的功能项。


一、CRT?

定向测试集得环境: 只需要施加激励,然后人工检查输出结果。正确得输出结果以保存为标准日志文件;
CRT 环境: 不仅需要产生激励,还需要参考模型,传输函数或其他方法预测输出结果。
CRT由两部分组成: 使用随机的数据流为DUT产生输入得测试代码,伪随机数发生器(PRNG)得种子(seed); 改变种子的值就可以调整测试。

二、什么需要随机化

产生随机化得技术产生激励,产生随机化的数据,调用$random 函数,但是这种方法只能找到数据路径方面的Bug。该方法的本质还是基于定向测试的方法。
因此随机化使DUT的控制路径里的每一个分支都被测试:
考虑设计输入的各个方面:

  1. 器件配置; 2、环境配置;3. 原始输入数据;4. 封装后的输入数据;5. 协议异常;6. 延时;7. 事务状态;8. 错误和违规;

1. 器件配置

DUT 实际使用是其内部有各种配置的,而不是刚刚退出复位状态的实际,仅仅用一个固定的初始化向量使设计进入到确定的状态。
要测试这个器件, 验证工程师必须写很多行TCL代码来配置每个通道,他只可能验证少数几个通道的配置。
如果采用CRT方法, 他只需要写一个针对一个通道的参数随机化的测试平台,然后把它放在一个循环里去配置整个器件。

2. 环境配置

通常你设计的器件 在一个包含了若干器件的环境里工作。当验证DUT时,你应该随机化整个环境,包括对象的数量以及他们如何配置。
例如,验证一个I/O交换芯片,把很多PCI总线连接到内部存储器总线上。 他们随机选择PCI总线的个数(1-4个)、每个总线上器件的个数(1-8个)、每个器件的参数(主、从、CSR地址)。随机化的测试几乎可以覆盖所有这些组合情况。

3. 原始输入数据

只要准备相关的事务类,但需要设计协议的各个曾测以及故障注入。

4. 封装后的输入数据

很多器件会处理吉利的不同层次。如 一个器件可能会产生TCP流量,TCP数据随后随后被封装到IP协议里,最后被放到以太网包里发送出去。协议的每个层次都有自己的控制域,可以采用随机化的方法测试不同的组合。你需要编写约束以产生有效的控制域,同时还允许注入故障。

5. 协议异常、错误 和 违规

测试设计规范之外的行为 和 测试设计在设计规范边界处的行为。
确认设计 正确处理故障,不会死锁 或 进入不正确的状态。

6. 延时

许多通信协议定义了延时的范围,例如 总线允许信号在总线请求信号1-3个时钟周期后到来,存储器的数据在4-10个总线周期后有效。
测试平台应该在每一个测试里使用随机的、有效的延时,一边发现设计中的Bug。
一些设计会对时钟抖动非常敏感。 通过把时钟沿来回移动一格很小的步长,可以检查设计是否对时钟周期的微小变化异常敏感。
时钟发生器应该具有一些可配置的参数,例如 频率和相位。这些参数可以有测试平台在配置过程中设置。
查找功能错误。测试平台不应该厂是违反建立时间和保持时间的约束。

三、SV中的随机化

首先建立一个具有一组相关的随机变量的类。你可以用约束来限制这些随机值的方位,使它们使有效的值,也可以测试某些专用的功能。

1. 带有随机变量的简单类

constraint c { src > 10;
               src < 15; }
这段代码是声明性质,而不是程序性质。

assert(p.randomize());
  else $fatal(0,"Packet::randomize failed");

randomize()函数在遇到约束方面的问题时返回0。使用断言来检查randomize函数的结果。

构造函数:是用来初始化对象的变量,不能再这里调用randomize()函数。
类里的所有变量都应该时随机的(random)和公有的(public),这样才能最大程度地控制DUT。
如果忘记把变量设置成随机地,就只能通过编辑环境变量来实现,避免这样做!

2. 检查随机化(randomize)地结果

使断言能够 终止 仿真过程。

3. 约束求解

约束表达式地求解 是由 SV的约束求解器完成的。求解器从一个初始值(seed) 产生。

4. 什么可以被随机化

SV可以随机化整型变量,即由位组构成的变量。
在约束中指向句柄,只能随机化2值数据类型,但位也可以是2值或4值类型,不能使用随机字符串。

四、约束(固定顺序、只能用关系运算符)

有用的激励并不仅仅是随机值—各个变量之间有着相互关系。

class child;
   bit [31:0] age; // 没有用rand 或 randc
   constraint c_teenager { age > 12;
                           age < 20;}
endclass

randomize( ) 函数会 为 随机变量选取一个新的值,必须保证满足所有的约束条件。 因为没用rand ,randomize()仅仅检查age的值是否在c_teenager约束定义的范围里。如果正好age在13-19里,那么randomize函数是正确的。

1. 什么是约束

constraint 里的表达式 最多只能用一个关系操作符(<,>,==,<=,>=)

typedef enum {READ,WRITE,IDLE,CONTROL} stim_e;
rand stim_e kind; //枚举变量
rand bit [31:0] len, src, dst;
const bit [31:0] const_addr = 42;
bit congestion_test;
constraint c_stim {
        len < 1000; //固定顺序的约束
        len > 0;  // 如使用 0 <len < 1000,这样做不对
        if (congestion_test) {
            dst inside {[const_addr-100, const_addr+100]}};
            src == const_addr; //约束块里只能包含表达式,所以在约束块里不能进行赋值。用关系运算符==可以。 如:len == header.addr_mode * 4 + payload.size()
            }
        else
           src inside {0, [2:10], [100:107]};
      }
endclass

2. 权重分布

dist 操作符允许产生权重分布

constraint cst {
      src dist {0:=40, [1:3]:=60}; // := 范围内的每一个值得权重是相同的
      // src = 0, weight = 40/220
      // src = 1, weight = 60/220
      // src = 2, weight = 60/220
      // src = 3, weight = 60/220

      dst dist {0/=40,[1:3]/=60}; // /= 范围内表示权重要均分给每一个值
      // dst=0, weight = 40/100
      // dst=1, weight = 20/100
      // dst=2, weight = 20/100
      // dst=3, weight = 20/100
}

值和权重 可以是常量 或 变量。设置权重为0,从而删除一个值。
动态改编权重:

typedef enum {READ,WRITE,IDLE} length_e;
rand length_e len;
bit [31:0] w_read = 1, w_write = 3, w_idle = 5;
constraint c_len {
       len dist { READ:=  w_read;
                  WRITE:= w_write;
                  w_idle:= w_idle;}
}

3. 集合(set)成员和 inside 运算符

集合里得值选取机会是相等的。

rand bit [6:0] b; // 0 <= b <= 127
rand bit [5:0] e; // 0 <= e <= 63
constraint c {
      b inside {[$,4],[20:$]};
      e inside {[$,4],[20:$]};
}

随机集合约束得取反

constraint c_range {
    ! (c inside {[lo:hi]}); // c < lo 或 c > hi
}

4. 在集合里使用数组

集合里的每一个值取出的概率是相同的。

rand int f;
int array[5] = `{1,2,3,4,5};
int array[] = `{1,1,1,23,4,5};
constraint c { 
     f inside array;
}
or 等价的约束
 constraint c {
   (f == array[0]) ||
   (f == array[1]) ||
   (f == array[2]) ||
   (f == array[3]) ||
   (f == array[4]) ||
}

如果想动态地向集合里添加或删除值,要经过三思后才能使用inside操作符,它会影响仿真器的性能。
randc变量的计算非常块。

5. 条件约束(关系操作 -> 和 if-else)

怎样才能让一个约束表达式只在某些时候才有效呢?
例如:一条总线 支持 字节、字、长字 的读操作,只支持 长字的写操作。

  1. -> 可以产生 和 case 一样的效果, 可以用于枚举类型的表达式。
class Busop;
...
   constraint c {
             (io_space_mode) -> addr[31] = 1'b1;
             }

约束: {(a ==1) -> (b ==0);} 和 { ! (a ==1) || (b ==0)} 约束等价的, 求解器并不是先计算 a1, 再令 b0; 而是 如下表所述:

ab
10
01
  1. if-else 更适合真假类型的表达式
class Busop;
...
   constraint c {
             if (op == READ)
                len inside {[BYTE:LWRD]};
             else
                len == LWRD
             }

6. 双向约束

约束块中的 内容是 声明性的代码,是并行的。所有的约束表达式同时有效。 约束是双向的,会同时计算所有随机变量的约束。

rand logic [15:0] r,s,t;
constraint cst{
         r < t;
         s == r;
         t < 30;
         s > 25;
    }

求解: 25 < s = r < t <30

6. 使用合适的数学运算来提高效率

约束求解器可以 有效地处理简单的数学运算:加、减、位提取、移位、
对于32位熟知的乘法、除法、和取模运算量 是非常大的。

位提取 代替 除法和取模运算

rand bit [31:0] addr;
constraint slow_near {
       addr % 4096 inside {[0:20],[4075:4095]};
       addr[11:0] inside {[0:20],[4075:4095]};
}

五、解的概率

SV 不能保证 随机约束求解器 能给出准确的解, 可以干预解的概率分布。

1. 关系操作和双向约束

(x == 0)-> (y 0), 当 x0时,则y=0;若y=0,则对x的值没有约束。若y不等于0,则x值为1.

2. 使用solve…before约束引导概率分布

rand bit x;
rnad bit [2] y;
constraint cstr {
(x == 0-> (y ==0);
solve x before y;
}

X 为0或1的概率相同
当 x0时,则y=0;
当 x
1时,则y为0,1,2,3的概率相等
若y=或不等于0,则对x的值没有约束

3. 控制多个约束块

用不同的约束块用于不同的测试

运行期间,使用内建的constraint_mode()函数打开或关闭约束。

class Packet;
  rand int length;
  constraint c_short {length inside [1:12];}
  constraint c_long  {length inside [1000:1023];}
endclass
Packet p;
initial begin
   p = new();
   //禁止c_short约束产生短包
   p.c_short.constraint_mode(0);
   assert (p.randomize());
   transmit(p);
   // 禁止所有的约束,使能短句约束来产生短包
   p.constraint_mode(0);
   p.c_short.constraint_mode(1);
   assert(p.randomize());
   transmit(p);
end

4. 有效性约束

设置多个约束以保证随即激励的正确性是一种很好的随机化技术,也称为有效性约束

class transaction;
   rand enum {BYTE,WORD,LWORD,QWRD} length;
   rand enum {READ,WRITE,RMW,INTR} opc;
   constraint valid_RWM_LWRD {
       (opc == RMW) -> (length == LWRD);
   }
endclass

5. 内嵌约束

class Transaction;
  rand bit[31:0] addr, data;
   constraint c1  {addr inside [0:100],[1000:2000];}
endclass
Transaction t;
initial begin
   t = new();
   assert(t.randomize() with { addr >=50; addr <=1500; data <10;});
   这里约束与transaction类的约束是取交集的,这里addr 而不是t.addr,用了类的作用域,{}用于声明性代码
   drive_bus(t);
 end

六. pre_randomize 和 post_randomize 函数 void类型

pre_randomize 在设置类里的一些非随机变量(例如上下限、权重);
post_randomize 需要计算随机数据的误差校正位
若在pre_randomize 和 post_randomize 函数 调用 调试程序,则调试程序必须是函数类型。

1. 构造浴缸型分布(不用rand)

在 pre_randomize函数计算指数曲线上的一个点,然后随机地选择把这个点放在左边或右边的曲线上。
Verilog提供了很多非线性分布的函数,例如$dist_exponential,但没有浴缸型分布函数
在这里插入图片描述

构造浴缸型分布
class Bathtub;
  int value; // 浴缸型分布的随机变量
  int WIDTH=50, DEPTH=4, seed=1;
  function void pre_randomize();
    //计算函数曲线
    value = $dist_exponential(seed, DEPTH);
    if(value > WIDTH) value = WIDTH;
    // 把这个点随机地放在左边或右边的曲线上
    if($urandom_range(1))
        value = WIDTH-value;
  endfunction
endclass
value 的值在每次对象随机化的时候更新,经过多次随机化后,就可以得到预期的浴缸型非线性分布。

2. void 函数

randomize函数调用pre_randomize函数,不调用消耗时间的任务,如果随机化过程中出现的问题,可以调用预先准备好的void类型的显示程序来显示中间结果。

3. 随机函数

  1. $random() 平均分布,返回32位有符号随机数
  2. $urandom() 平均分布,返回32位无符号随机数
  3. $urandom_range() 在指定范围内的平均分布
  4. $dist_exponential() 指数衰退
  5. $dist_normal() 钟型分布
  6. $dist_poisson() 钟型分布
  7. $dist_uniform() 平均分布

六 约束的技巧和技术 demo

怎样编写易于修改的CRT?
DEMO:

1. 使用变量的约束,使用非随机值

可以用rand_mode(0)函数把变量设置为非随机变量.

产生变长负载的包
class Packet;
   rand bit [7:0] length, payload[];
   constraint c_valid {
      length > 0;
      payload.size == length; }
endclass

Packet p;
initial begin
  p =new();
  //随机化所有变量
  assert(p.randomize());
 p.display("Simple randomize")
 p.length.rand_mode(0);  // 设置包长为非随机值
 p.length = 42; // 设置包长为常数
 assert(p.randomize()); //在随机化paypload,
 注意包长不随机化
end

2. 用约束来检查值得有效性

调用 handle.randomize(null) 函数,SV会把所有的变量当作非随机变量,仅仅检查这些变量是否满足约束条件。

3. 随机化个别变量

randomize()函数只传递变量得一个子集,这样就只会随机化类里得几个变量,其他变量会被当作状态变量(非随机变量),所有的约束仍然保持有效。

class rising;
  byte low;          // 非随机变量
  rand byte med, hi; // 随机变量
  constraint c { low<med; med<hi;}
endclass

rising r;
initial begin
    r = new();
    r. randomize();   // 随机化med,hi,不改变low
    r.randomize(med); // 随机化med
    r.randomize(low); // 随机化low,非随机变量
end

4. 打开或关闭约束

  1. 约束得表达式越来愈多,常见得办法是对每一种指令建立一套独立的约束,在使用时关闭其他所有的约束。
class Packet;
  rand int length;
  constraint c_short {length inside [1:12];}
  constraint c_long  {length inside [1000:1023];}
endclass
Packet p;
initial begin
   p = new();
   p.constraint_mode(0);  //禁止所有约束
   p.c_short.constraint_mode(1); 
   assert (p.randomize());
   transmit(p);
   // 禁止所有的约束,使能长句约束来产生短包
   p.constraint_mode(0);
   p.c_long.constraint_mode(1);
   assert(p.randomize());
   transmit(p);
end
  1. 也可以用条件约束,如if-else

    5. 在测试过程中使用内嵌约束

    有效性约束

    6. 在测试过程中使用外部约束

外部约束放在另一个文件里,在不同的测试里可以服用外部约束。
只能增加约束,不能改变已有的约束。

// packet.sv
class Packet;
   rand bit [7:0] length;
   rand bit [7:0] payload[];
   constraint c_valid { length > 0;
                        payload.size() == length;}
   constraint c_extern;
endclass
//test.sv
program automatic test;
  include "packet.sv" constraint Packet::c_extern{ length ==1;}
...
endprogram

7. 扩展类

扩展类得约束与基类得约束得名字相同,那么扩展得约束将取代基类得约束。

七 随机化的常见错误

1. 小心使用有符号变量

除非必要,不要在随即约束里使用有符号类型。
byte 有符号,[-127,127]
bit 无符号,[0,127]

当然还有可能 bit1+bit2 =64 ,其中bit1和bit2均为8位位宽时,这样就可能导致 8位加8位是有可能9位的,这样写就不对,应该这样写:bit1+bit2 =9’d64 ;

2. 提高求解去性能的技巧

避免使用复杂的运算,如除法、乘法、取模
小于32位的变量可以得到更高的运算性能

3. 迭代和数组约束

之前都是约束标量类型的变量。在随机化数组时进行约束:用foreach和一些数组函数;
但是foreach会产生很多约束,影响仿真器的运行速度。最好用randc变量代替嵌套的foreach算法。

4. 约束动态数组的大小

class dyn_size;
   rand logic [31:0] d[];
   constraint d_size {d.size() inside{[1:10]};}
endclass

使用inside {[1:10]} 设置上下限,一般不希望数组尺寸为0,上限不要太高,产生成千上万个元素,导致求解器要很长的时间才能求解。

5. 元素的和

在这里插入图片描述
图中 是在十个周期内发送四个数据的脉冲信号示意图

随机化脉冲类
parameter MAX_TRANSFER_LEN = 10;

classs strobepat;
    rand bit strobe[MAX_TRANSFER_LEN];
    constraint c_stobe { strobe.sum() == 4'h4; }
    用sum()函数约束随机数组元素个数最大是10
endclass
initial begin
   strobepat sp;
   sp = new();
   int count = 0;  // 数据数组的索引
   assert(sp.randomize());
   foreach (sp.strobe[i]) begin
       @bus.cb;
       bus.cb.strobe <= sp.strobe[i];
       // 如果strobe信号有效,输出下一个数据
       if(sp.strobe[i])
          bus.cb.data <= data[count++];
    end
end

6. 数组约束的问题

假设要产生1-8个随机事务,这些事务的总长度小于1024;
方法1和2:

class sum1024;
   rand bit [7:0] len[];  // rand byte len[]; 和有时会为负,并且始终小于127
   constraint c_len { len.sum < 1024;
                      len.size() inside {[1:8]};}
endclass
这个和不为负了,但是始终小于256. 原因是 8位数值的和 用 8位数值保存。

方法3:

class sum1024;
  rand unit len[];  // 32 位
  constraint c_len { len.sum < 1024;
                     len.size() inside {[1:8]};}
endclass
len的变量都超过8位,

方法4:

class good_sum4;
  rand unit len[];
  constraint c_len { foreach(len[i])
                        len[i] inside {[1:255]};
                        len.sum <1024;
                        len.size() inside{[1:8]};}
endclass

使用foreach产生递增的数组元素的值

class asecond;
  rand unit d[10];
  constraint c {
      foreach (d[i])    //对数组的每个元素操作
         if(i>0)        //除了第一个元素
            d[i]>d[i-1] //和前一个元素比较
   }

6. 产生具有唯一元素值的数组

方法1

class uniqueslow;
  rand bit [7:0] ua [64];
  constraint c {
      foreach (ua[i])
         foreach (ua[j])
            if(i != j)
               ua[i] != ua[j];
}
endclass

方法2

class randc1;
    randc bit [7:0] val;
endclass
class littleuniquearray;
    bit [7:0] ua [64];  //每个元素具有唯一值的数组
    function void pre_randomize;
        randc1 rc;
        rc = new();
        foreach (ua[i]) begin
           assert(rc.randomize());
           ua[i] = rc.val;
        end
    endfunction
endclass

7. 随机化句柄数组

随即求解器不会创建对象。

产生随机数组的元素
parameter MAX_SIZE = 10;

class Randstuff;
    rand int value;
endclass
class Randarray;
    rand Randstuff array[];  // 不要忘记使用rand
    constraint c {array.size inside {[1:MAX_SIZE]};}
    function new();
       array = new[MAX_SIZE];  // 按最大的容量分配
       foreach (array[i])
          array[i] = new();
     endfunction
endclass
Randarray ra;
  initial begin
     ra = new();   //构造数组和所有的对象
     assert(ra.randomize());
     foreach(array[i])
         $display(ra.array[i].value);
  end

八 产生原子激励和场景

每次只产生一个事务无法模拟出这些场景

1. 和历史相关的原子发生器

产生事务流的最简单的办法是基于以前事物的随机值的原子发生器。

2. 随机序列

产生事务序列的另一个方法是使用SV的randsequence结构。

initial begin
    for (int i=0; i<15; i++) begin
        randsequence (stream)
           stream: cfg_read := 1 |
                   io_read  := 2 |
                   mem_read := 5;
           cfg_read: { cfg_read_task; } |
                     { cfg_read_task; } cfg_read;
           mem_read: { mem_read_task; } |
                     { mem_read_task; } mem_read;
           io_read:  { io_read_task;  } |
                     { io_read_task;  } io_read;
       endsequence
   end //for
end

task cfg_rea_task;
    ...
endtask

cfg_read 可以对cfg_read_task任务的一次调用, 后边跟着 cfg_read,所以后边还可以调用cfg_read_task,至少一次;

randsequence 优点:程序性代码,执行过程中可以逐步调试,增加$display语句。若调用对象的randomize()函数,无法知道其是否执行。

randsequence 缺点:修改一个序列,如增加一个新的分支或动作,你可能需要改变序列的原始代码,而不能通过扩展序列来实现。

3. 随机对象数组

产生随机序列的形式:随机化整个对象数组。可以建立之下该数组的前一个和后一个对象的约束。同时求解所有的约束。

4. 随机控制

使用randcase 和 $urandom_range的随机控制

initial  begin
  int len;
  randcase
     1: len = $urandom_range(0,2); // 10%: 0,1 or 2
     8: len = $urandom_range(3,5); // 80%: 3,4 or 5
     1: len = $urandom_range(6,7); // 10%: 6 or 7
  endcase
  $display("len = %0d", len);
  end

$urandom_range(15), 则等价于$urandom_range(0,15)

class lendist;
   rand int len;
   constraint c { len dist{ [0:2]:= 1,
                            [3:5]:= 8,
                            [6:7]:= 1 };  }

randcase 和 constraint中的 dist是一样的功能,但是randcase想要修改代码比随机约束更难修改和重载。

5. 用randcase建立决策树

initial begin
  // level 1 
  randcase
     one_write_wt: do_one_write;
     one_read_wt:  do_one_read;
     seq_write_wt: do_seq_write;
     seq_read_wt:  do_seq_read;
  endcase
end
//level 2
task do_one_write;
     randcase
        mem_write_wt: do_mem_write;
        io_write_wt:  do_io_write;
        cfg_write_wt: do_cfg_write;
     endcase
endtask
task do_one_read;
    randcase
        mem_read_wt: do_mem_read;
        io_read_wt:  do_io_read;
        cfg_read_wt: do_cfg_read;
    endcase
endtask

5. 随机数发生器

  1. 简单的伪随机数发生器
reg [31:0] state = 32'h12345678;
function logic [32] my_random;
  logic [64] s64;
  s64 = state * state;
  state = (s64>>16) + state;
  my_random = state;
endfunction
简单的Verilog的PRNG,有一个32位的内部状态。要计算下一个随机值,先计算出状态的64位平方值,取中间的32位数值,然后加上原来的32位数值。
  1. 随机稳定性-------多个随机发生器
    SV 测试平台总通常会有几个激励发生器同时运行。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    SV 的每个第项都有自己的PRNG和独立的种子,当启动一个新的对象或线程时,子PRNG的种子由父PRNG产生。所以仿真开始时的一个种子可以产生多个随机激励流,他们之间又是相互独立的。
  2. 随机稳定性和层次化种子
    测试平台首先创建了对象,然后再并行的线程里运行他们,
    随后增加了一个新的发生器,并在新的线程里运行,新的对象在原来的对象之后创建,新的线程也是在原来的线程之后产生
  3. 随机器件配置
    测试DUT的一个重要工作 是 测试DUT内部设置和环绕DUT的系统的配置。
    如何建立测试平台配置,并在必要的时候在测试级改变配置。
以太网交换机配置类
class eth_cfg;
   rand bit [3:0] in_use;   //测试中使用的端口
   rand bit [470] mac_addr[4]; // MAC地址
   rand bit [3:0] is_100; // 100MB 模式
   rand unit run_for_n_frames; // 测试中的帧数
   // 在unicast模式时设置某些地址位
   constraint local_unicast {
          foreach(mac_addr[i])
               mac_addr[i][41:40] = 2'b00;
    }
    constraint reasonable { //限制测试长度   
       run_for_n_frames inside {[1:100]};             
    }
endclass: eth_cfg
class Environment;
   eth_cfg;
   eth_src gen[4];
   eth_mii drv[4];
   
   function new();
       cfg = new();  // 创建cfg
   endfunction
   function void gen_cfg;
      assert(cfg.randomize());  //随机化cfg
   endfunction
   //使用随机配置建立环境
   function void build();
       foreach (gen[i]) 
          if (cfg.in_use[i]) begin
             gen[i] = new();
             drv[i] = new();
             if(cfg.is_100[i])
                drv[i].set_speed(100);
          end
   endfunction
   task run();
    foreach (gen[i])
       if (cfg.in_use[i]) begin  //必须所有用到gen[i]的地方都先检测in_use[i],否则当测试平台访问到不存在的发生器时会崩溃。所以先检测
       //启动测试平台的事务处理器
         gen[i].run();
       ..
       end
   endtask
   task wrap_up();
   endtask
endclass:Environment

测试例化了环境类,然后一次运行。

program test;
   Environment env;
   initial begin
      env = new();  // 创建环境
      env.gen_cfg;  // 建立随机配置
      // 修改随机值-打开四个端口
      env.cfg.in_use = 4'b1111;
      env.build();  // 激励测试平台的环境
      env.run();    // 运行测试
      env.wrap_up();// 整理 & 产生报告
   end
endprogram

总结

CRT 时产生验证复杂设计所需激励的唯一可行的方法。

测试必须是灵活的,允许你既可以使用产生的缺省值,也可以约束或修改。

建立测试平台前务必事先规划,流出足够的“钩子”,这样才能在不修改现有代码的情况下控制测试平台。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值