第七章:线程以及线程间的通信(续)

所有的数据交换和控制同步被称为线程间的通信(IPC)。在SystemVerilog中可以使用事件、旗语和信箱来完成。

7.3 事件

在Verilog中,@操作符是边沿敏感的,它总是阻塞着,等待着事件的变化。其他的线程可以通过->操作符来触发事件。
SystemVerilog引入了triggered()函数,可用于查询某个事件是否已经被触发,包括在当前时刻。线程可以等待这个函数的结果,而不用在@操作符上阻塞。

7.3.1 在事件的边沿阻塞

边沿触发
结果:(有竞争)
@0: 1: before trigger
@0: 2: before trigger
@0: 1: after trigger

7.3.2 等待事件的触发

使用电平敏感的wait(e1.triggered())来替代边沿敏感的阻塞语句@e1.
等待事件
结果:(有竞争)
@0: 1: before trigger
@0: 2: before trigger
@0: 1: after trigger
@0: 2: after trigger

7.3.3 在循环中使用事件

使用事件来实现两个线程的同步,但是务必要小心避免零延时循环。
等待事件导致零延时循环

  • wait(handshake.triggered());使得循环在单个事件触发器上反复执行。
  • 采用等待事件的边沿可以避免零延时。
    等待事件的边沿
7.3.4 传递事件

SystemVerilog允许你在对象间共享事件,事件成为同步对象的句柄,可以传递给子程序。

7.3.5 等待多个事件

测试环境必须等待多个子线程的完成,利用wait fork看上去应该是最容易的解决方法。但是这样的话,你需要等待其衍生出来的许多子线程。这并不是我们所期望的。下面介绍几种方法来实现等待多个事件。
方法一:创建一个新的线程并从中衍生出来子线程,然后保证每个线程阻塞在每一个发生器的一个事件上。
使用wait fork等待多个线程
方法二:记录已经触发的事件的数目。
对触发事件进行计数来等待多个线程
方法三:摆脱所有事件而仅对运行着的发生器进行计数。选用静态变量来存储计数值。
使用线程计数来等待多个线程

7.4 旗语

可以将旗语看成是一把钥匙,谁拥有钥匙谁就对资源具有使用的权利。旗语可以被视为一个互斥体,用于实现对同一资源的访问控制。

  • 一个线程如果请求钥匙而得不到的话,就会一直阻塞。多个阻塞的线程会以先进先出(FIFO)的方式进行排队。
  • 如果只有一把钥匙,第一个线程请求两把,第二个线程请求一把,这时先进先出的原则就会被忽略掉,第二个线程会排在第一个线程前面。
  • 你返回的钥匙可以比你取出来的多。
    用旗语实现对硬件资源的访问控制
  • semaphore sem;创建一个旗语
  • sem = new(1);分配一把钥匙
  • sem.get(1);获取一把钥匙
  • sem.put(1);返回一把钥匙
  • try_get();获得钥匙而不被阻塞,返回1表示有足够多的钥匙,返回0则表示钥匙不够。

7.5 信箱

如何在两个线程之间传递信息呢?直观的想,发生器创建的事务并传递给驱动器的情况,如果直接让发生器线程去调用驱动器中的任务就可以了。但是这样带来两个问题,第一,发生器需要到达驱动器任务的层次化路径,这会降低代码的可重用行,第二,这样会使得一个发生器需要控制多个驱动器,从而引发同步的问题。
SystemVerilog使用了信箱。从硬件的角度来讲,可以把信箱看成一个FIFO。
连接两个事物处理器的信箱

  • 信箱是一种对象,必须使用new函数来进行实例化。特别是在循环创建随机变量时,需要每一次都对信箱创建一个对象。
  • put任务可以将数据放入信箱,get任务可以取出来信箱里的数据。peek任务可以获取对信箱里数据的拷贝而不移除它。try_get()和try_peek()能够防止阻塞情况的发生,成功时返回一个非零值,否则返回零。
  • 一个信箱里只放一种类型的数据。
  • 缺省容量是0,表示信箱不限容。任何大于零的容量便可以创建一个定容信箱。
7.5.1 测试平台的信箱

一个发生器、一个驱动器使用信箱和顶层程序实现数据交换的过程。
发生器
驱动器
顶层结构

7.5.2 异步线程间使用信箱通信

如果你想要生产方和消费方两个线程一致,那就需要额外的握手信号。
下面介绍几种实现线程同步的方法:
方法一:使用定容信箱和peek来实现线程同步。
发生器
在消耗方使用peek来同步线程

  • mbx.get(i),移除以后,mbx.put阻塞解除,就会立马产生一个新数据,这样生产方可能会在消费方完成事务处理之前就生成新数据。而改用peek以后,等消费方处理完数据后,再而使用get移出,从而生产方产生一个新的数据。

方法二:使用信箱和事件来实现线程同步。

使用事件实现同步生产方和消费方
方法三:使用两个信箱来实现线程的同步。
使用两个信箱来实现线程的同步

原文地址:https://www.cnblogs.com/xuqing125/p/9556452.html