高级验证方法学()-Mentor-笔记

来源:互联网 发布:网络的个性化服务 编辑:程序博客网 时间:2024/06/08 14:30

1、事物级组件间一般用fifo作为通信通道

2、sv中,参数或局部参数优先于宏,这样可以减少编译的次数

3、验证方案从设计规范来,验证方案有一个问题清单,列举所有验证过程需要回答的问题,和说明如何被回答的机制的描述。此外还有一个checklist,列举所有需要回答的问题,它也是测试平台的功能规范

4、systemC不适合构建低层次验证平台

5、激励生成方式:随机、定向、定向随机,运行方式:自由运行、受控制运行、独立运行、同步运行

定向/定向随机激励:一般由特定发生器 ,以便在DUT上执行一个特定的功能或运行一个特殊的情节 。
6、Master:发出请求并接收响应。主器件引发行为,依据响应来确定其下一阶段的动作

Slave:接受请求并返回响应

7、has-a:当一个对象是另一个对象的引用(reference)或指针(pointer)时,这两个对象就通过HAS-A关联 。eg:A有一个B

is-a:由继承产生的对象通过IS-A构成。继承对象就认为是子类,或者是父对象的一个特殊版本 。eg:primates is-a Mammalia


systemverilog中解释如下图:


8、验证架构组件尽量选用小的、带有定义好的接口的组件来实现,会变的简单起来,若能达到复用这些组件,则实现了进一步简单

9、事务:面向硬件: 在一个设计中发生在两个时间点的所有事件,另一种说法:两个实体间单向转移的控制或数据。

面向软件:一个事务就是一个函数的调用。

10、参考模型构建相比DUT要:执行更快、成本更低,所以=>事务级,

11、生产者/消费者模型:

put():指数据方向,由producer发出put指令,数据从produce->consumer





产生器调用消耗器中的一个函数称作put。Put函数的目的就是将数据从发生器的范围传送到consumer的范围。发生器以实际参数的形式给put函数提供数据。Put函数在consumer的范围内运行,因此一旦调用put(),其参数中的数据就传送到consumer的范围。

get():仍然指数据方向,但也还是producer发出get指令,数据从consumer->producer,但实际工作上producer是consumer。




module top;

   producer p;


请求/响应:producer通过put向consumer发送一个值,而consumer接受到值执行完后向producer返回一个响应,在这个过程中,他们都是生产者和消费者。在这个过程中,consumer作为从机是要消耗时间的,而producer将处于阻塞状态知道从机返回响应。





12、FIFO


组件间通信控制。以producer/consumer为例,可以各自独立运行,直至对应的FIFO满时才会发生堵塞,当FIFO有位置时又恢复执行。而且,从一定程度也有简化作用:每个组件只针对FIFO通信,不需要关心具体是哪个组件。

此时组件间的阻塞是以FIFO的形式阻塞,即FIFO的空满。

class producer extends avm_verification_component;
 tlm_blocking_put_if#(int) put_port;
 task run;
  int randval;
  for(int i = 0; i < 10; i++)
  begin
  randval = $random % 100;
  $display(“producer: sending %4d”, randval);
  put_port.put(randval);
  end
 endtask
endclass : produce

//

class consumer extends avm_verification_component;
 tlm_blocking_get_if#(int) get_port;
 task run;
  int val;
  forever
  begin
  get_port.get(val);
  $display(“consumer: receiving %4d”, val);
  end
  endtask
endclass : consumer

//

module top;
 producer p;
 consumer c;
 tlm_fifo#(int) f;//指向一个参数化类的指针,
 initial
 begin
 // instantiate the producer, consumer,
 // and the fifo channel
  p = new;
  c = new;
  f = new;
 // connect the producer and consumer
 // through the fifo channel
  p.put_port = f.blocking_put_export;
  c.get_port = f.blocking_get_export;
 // kick off the run processes in each
 // verification component
  avm_verification_component::run_all();// AVM-lib
 end
endmodule : top

13、双向通信:组件间两个FIFO


双向通讯中的事件发生顺序如下:主机通过使用put()发送请求给从机发起通讯。从机用get()找回请求。从机发送请求并表示响应。一旦形成响应,从机也用put()将响应返回给主机。最后,响应被主机用get()找回。
class bidir_env extends avm_env;
 master m;
 slave s;
 tlm_req_rsp_channel #( int ) req_rsp;
 function new;
  m = new(“master”);
  s = new(“slave”);
  req_rsp = new(“req_rsp_channel”);
 endfunction
 function void connect;
  m.req_port = req_rsp.blocking_put_request_export;
  m.rsp_port = req_rsp.blocking_get_response_export;
  s.req_port = req_rsp.blocking_get_request_export;
  s.rsp_port = req_rsp.blocking_put_response_export;
 endfunction
 task execute;
  #10;
 endtask
endclass // bidir_en

//

class master extends avm_verification_component;
 tlm_blocking_put_if #( int ) req_port;
 tlm_blocking_get_if #( int ) rsp_port;
 function new( string name , avm_named_component parent = null );
  super.new( name , parent );
 endfunction // new
 task run;
  fork
  request_process;
  response_process;
  join
 endtask
 task request_process;
  string request_str;
  for( int i = 0; i < 10; i++ ) begin
  $sformat( request_str , “%d” , i );
  avm_report_message(“sending request” , request_str );
  req_port.put( i );
  end
 endtask // request_process
 task response_process;
  int response;
  string response_str;
  forever begin
  rsp_port.get( response );
  $sformat( response_str , “%d” , response );
  avm_report_message(“recieving response” , sponse_str );
  end
 endtask
endclass // master

//

class slave extends avm_verification_component;
 tlm_blocking_get_if #( int ) req_port;
 tlm_blocking_put_if #( int ) rsp_port;
 function new( string name , avm_named_component parent = null );
  super.new( name , parent );
 endfunction // new
 task run;
  int request , response;
  string request_str , response_str;
  forever begin
  req_port.get( request );
  $sformat( request_str , “%d” , request );
  avm_report_message(“recieving request” , request_str);

response = request;
  $sformat( response_str , “%d” , response);
  avm_report_message(“sending response” , response_str );
  rsp_port.put( response );
  end // forever begin
 endtask
endclass

14、仿真运行慢:仿真中发生的事件越多,调用的程序越多,越慢

15、sv interface是一种即时阻塞 ,即一组wire。

一个虚拟接口是一个接口的指针,虚拟接口指向真正的接口使得事务器能够再利用,事务器并不需要设计的是什么,

只需要知道接口即可

纯虚接口类:接口类是基类的抽象。通常,它的所有办法都是纯虚的。 (参见producer/consumer)

16、SystemVerilog测试平台中的所有基于类的验证组件都包含在环境类中。通常,任何环境类的仅有的2个公开可视的方法是

constructordo_test任务

17、激励发生器也可以用来生成测试开始前的初始化序列。

特别需要注意的是:当前事务不会被下一个事务的随机化意外覆盖。

18、analysis_port:


在测试进行前,每个用户对象必须在发行对象处注册,发行对象则保留一个用户对象的清单 。

19、通过目录或bin计算事件,这就叫binnng 

20、约束的随机验证 技术基础:

(1)实际随机数产生(RNG)程序 ,涉及到种子的选择和稳定性等其他方面

(2)是实际的约束求解器。求解器确定是哪一套值可能满足给定的约束,如果有的话。这些值就称为求解空间求解器首先计算求解空间,然后利用随机数发生器选择求解中的一个。

伪随机数产生随机数的程序并不是真正完全随机的。实际上,它是一个可预言的数的序列 。发生器看起来是随机的,因为这个序列需要很长时间才会重复 。

从DEBUG角度考虑,应该使用伪随机,在修改完成问题后重启仿真会产生相同的随机数序列,以便问题重现或校验设计。eg:



为灵活实现上述功能,可以采用随机种子,当传入种子不变,则每次产生相同的随机序列。eg:


随机过程中存在的干扰:eg:

 int A, B;
 initial repeat (5)
 begin

  A =$urandom;
  B =$urandom;
  $display(A);
 end

//

A的随机数序列会受到在下面第5行的,称作$urandom的干扰。

避免这种干扰发生的方法是对每一次调用$urandom用不同的变量手动赋初值。每个变量必须有一个唯一的值,否则每个RNG会产生相同的随机值序列。另一个解决这个问题的方法是给每一个RNG定义一个程序,自动产生独立的初始值。

随机稳定性:

修改激励会影响到随机的稳定性。

每个子系统、线程(initial、always块等)等内的随机都是从本线程内部获得根种子,然后用RNG生成随机序列,也就是说在一个子系统/线程内部只存在一个根种子,若同时启动多个随机化,他们之间将会互相影响,随机序列也可能相同。如上述例子所示就是这种情况。改进的方法就是放到不同的线程中,每个都从不同的根种子起始。eg:


//A和B的随机序列互不干扰

随机至少含有一个约束,显式或隐式的。

以对象为导向的随机(CSV):

将随机变量,随机数发生器和约束器整合一起形成的。

pre_randomize()和post_randomize():虚函数,可以被重写,但一旦被重写,则必须调用super.pre/post_randomize。若类中显式写出了他们的定义,在randomize()前后就会被自动调用。

随机约束:"->"、"|->"、inside、solve...before...、dist、":="、

动态数组被声明为随机变量时,它们就被认为是固定大小的数组。若涉及到类的句柄,则情况变的复杂一点。因为一个类句柄的动态数组必须至少在两个阶段进行随机化以确保正确的构造类对象,此时就需要使用到pre_randomize()和post_randomize():使用pre_randomize来求解大小也可以用主要的随机化方法来求解元素,也可以使用主要随机化方法(randomize)来求解大小,用方法post_randomize来求解元素。eg:

//post_randomize




21、

22、

23、

原创粉丝点击