SV基础知识5---线程与线程间的通信

目录

1 线程的使用

2 线程的控制

wait  fork  和  disable  fork  

3 线程间的通信

概述 

event事件  (@边沿阻塞和wait触发  通知的需求)

semaphore旗语(semaphore的操作  资源共享的需求)

mailbox信箱(mailbox信箱的操作  数据通信的需求)

通信要素的比较


1 线程的使用

线程就是独立运行的程序。verilog中对initial语句块主要有两种分组方式:

begin ...end:中的语句顺序执行

fork...join中的语句以并发方式执行

2 线程的控制

在sv中,新增了fork...join_any   fork...join_none   语句,其区别如下图

SV基础知识5---线程与线程间的通信_第1张图片

wait  fork  和  disable  fork  

wait  fork :等待所有子线程结束

SV基础知识5---线程与线程间的通信_第2张图片

disable 线程名:指定需要停止的线程

disable fork:停止当前线程中衍生出来未执行的所有子线程。

SV基础知识5---线程与线程间的通信_第3张图片

注意:若没有disable,则task会结束,但内部未完成的线程仍将执行。

3 线程间的通信

概述 

测试平台中所有的线程都需要同步并交换数据。

一个线程需要等待另一个线程--------------->> 事件event

多个线程可能同时访问同一个资源-------->> 旗语semophore

线程之间可能需要交换数据---------------->> 信箱mailbox

所有这些数据交换和同步称为线程间的通信(IPC Interprocsss Communication)

event事件  (@边沿阻塞和wait触发  通知的需求)

event事件是静态对象,用于事件的同步,它不需要new()函数

触发事件的操作符 ->,   它不会阻塞线程, 通常使用在等待事件之后

等待事件操作符分为两种:@边沿敏感和wait电平敏感

  • @边沿敏感:它总是阻塞着,等待事件的变化,只有事件发生变化时,线程才会继续,所以                          在同一时刻,有可能引起竞争现象
  • wait电平敏感: 由于存在delta cycle时间差,可以使用wait(event.triggered())来代替边沿敏感的阻塞语句@event。 (这个方法比起@等待事件,可以避免在相同时刻触发event而带的竞争问题,但同样无法捕捉已经被触发,但后续才等待的事件)

semaphore旗语(semaphore的操作  资源共享的需求)

semaphore可以实现对同一资源的访问控制,通常来说应该遵循互斥访问原则,避免出现多个线程同时访问同一资源,进而导致的线程异常。

存中创建semaphore时,类似于创建了一个篮子(bucket),篮子中包含了一定数量的钥匙(keys),要获取共享的资源,必须从篮子中获取一把或多把钥匙,在获取完资源后必须将钥匙放回到篮子中;如果此时另一个线程试图获取篮子中的资源,必须等到桶中有足够多的钥匙的时候才能进行,否则会一直阻塞在这里。

semaphore是SV内建的类,因此也需要声明句柄(即,创建旗语)和调用new()函数

主要有三种基本操作方法:

  • new() 方法可以创建一个包含特定钥匙的旗语对象,默认为0把
semaphore sem;  //创建一个semaphore(声明一个句柄)
semaphore_name = new(1);  //分配一把钥匙
  • get()可以获取一把或多把钥匙,默认1把
  • put()可以返回一把或多把钥匙,默认1把
sem.get(1);  //获取一把钥匙
...
sem.put(1);   //处理完事务后,放回钥匙

如果想要获取一把钥匙而不希望被阻塞,可以使用try_get()函数,它返回1表示有足够多的钥匙,返回0则表示钥匙不够

mailbox信箱(mailbox信箱的操作  数据通信的需求)

线程之如果传递信息,可以使用mailbox.。Mailbox也是SV内建的类,也需要声明句柄(即,创建邮箱)和调用new()函数来例化,若不指定大小,则信箱容量为无限大。

mailbox的操作方法也分为阻塞和非阻塞:

  • 阻塞方法:put()把数据放入mailbox          使用get()把数据从信箱中移除。                                          如果信箱为满,则put()会阻塞;如果信箱为空,则get()会阻塞。

                         peek()可以拷贝信箱中的数据而不移除它,若信箱为空,也会阻塞。

  • 非阻塞方法:try_put()  try_get()  try_peek() 这些方法都不会被阻塞

mailbox和queue有相近之处  :

  • mailbox必须通过new()例化,而队列只需声明
  • mailbox可存储不同数据类型(不建议),队列只能存储相同类型的数据
  • mailbox中的put()和get()是阻塞方法。而队列对应的存取方式为push_back pop_front()为非阻塞,会立即返回 ,在使用queue取数时应先填写 wait(queue.size()>0)。此外,还需注意,如果调用阻塞方法应使用task,非阻塞方法既可以用task也function   
  • 对于变量操作,在传递形式参数时,mailbox传递的是指针。而queue的形式参数声明是ref方向                                         

其他特性:如果要显示的限定mailbox中的数据类型,可以使用mailbox #(type = T)方式来声明

通信要素的比较

sv中的三种通信方式event、semaphore、mailbox都可以来解决进程间同步问题,但分别适用于不同的场景需求

  • event:最小信息量的触发,即单一的通知功能
  • semaphore:共享资源的安全卫士
  • mailbox:精小的sv原生FIFO,线程之间的数据通信和数据缓存

你可能感兴趣的:(systemverilog,多线程,systemverilog)