线程控制
发生器把激励传给代理时,环境类需要知道发生器什么时候完成任务,以便及时终止测试平台中还在运行的线程,这个过程就需要借助线程间的通信来完成。常用的线程间通信有事件控制、wait语句、SV信箱和旗语等。
Verilog对语句有两种分组方式,begin…and或者fork…join。前者是顺序方式执行,而后者是以并发方式执行的。
并行控制
- fork…join:需要所有并行的线程都结束以后才会继续执行
- fork…join_any:会等到任何一个线程结束以后就继续执行,即在调度块内语句,当第一个语句完成后,父线程才继续执行,其他停顿的线程也得以继续
- fork…join_none:不会等待其子线程而继续执行,即在调度块内其他语句时,父线程继续执行
时序控制
- SV可以通过延迟控制或者事件来对过程块完成时序控制
- 延迟控制即通过#来完成
#10 rega=regb;
- 事件(event)控制即通过@来完成
@r rega=regb;
@(posedge clock) rega=regb;
- wait语句也可以与事件或者表达式结合来完成
real AOR[];
initial wait (AOR.size()>0);
线程间的通信
- 测试平台中的所有线程都需要同步并交换数据
事件
事件成为同步对象的句柄,可以传递给子程序。这个特点允许在对象间共享事件,而不用把事件定义成全局的。最常见的方式是把事件传递到一个对象的构造器中。
- 可以用event来声明一个变量,并且使用->来触发它
- 也可以使用wait来进行边沿触发避免竞争
- wait_order可以使得进程保持等待,直到参数列表中的事件按照顺序从左到右依次完成
- 如果参数列表中的事件被触发但是没有按照要求的顺序,那么会使得等待操作失败
旗语(semaphore)
可以使用旗语实现对同一资源的访问控制。类似于车钥匙,用车钥匙人才可以使用汽车
semaphore sem; //创建一个旗语
- 使用new方法可以创建一个或单个或多个钥匙的旗语
sem=new(1); //分配一个钥匙
- 使用get可以获取一个或多个钥匙(阻塞型)
sem.get(1); //获取总线钥匙
- 使用put可以返回一个或多个钥匙
sem.put(1); //处理完成时把钥匙返回
- 如果试图获取一个旗语而不希望不被阻塞,可以使用try_get()函数(非阻塞型),它返回1表示有足够多的钥匙,而返回0表示钥匙不够
- keyCount指定从旗语获取所需钥匙数,默认值为1
- 旗语的等待队列是先进先出,即先排队等待旗语的将优先得到钥匙。如果指定数量的钥匙可用,则返回正数并继续执行,如果指定数量的钥匙不足,则该方法返回0
信箱(mailbox)
- 信箱可以使得进程之间的信息得以交换,数据可以由一个进程写入信箱,再由另外一个进程获得
- 信箱在创建时可以限制其容量或者不限制
- 当信箱量写满时,后续再写入的动作会被挂起,直到信箱的数据从中读取,使得信箱有空间以后才可以继续写入
- 不限制容量的信箱则不会挂起写入信箱的动作
- 创建信箱:new()
- 将信息写入信箱:put()
- 试着写入信箱但不会阻塞:try_put()
- 获取信息:get()同时会取出数据,peek()不会取出数据
- 试着从信箱取出数据但不会阻塞:try_get()/try_peek()
- 获取信箱信息的数目:num()