067~072 线程简介

一、线程的使用

1、什么是线程:线程,thread



二、线程的控制

ps:fork join/join_any/join_none内部的线程被称作块内,对应的外部则为块外

1、fork join:块内包含的所有的线程执行结束后,阻塞打开,才能继续执行块外的线程;

2、fork join_any:块内只要有一个线程执行结束,阻塞打开,就可以继续执行块外的线程,并且块内未完成的线程最终都会执行结束,结束后总的最外层的大线程才能结束;

3、fork join_none:开辟新的线程,只是提起一个子线程,然后就继续执行块外的线程,即块外的主线程不会被阻塞;

4、wait_fork:等待所有衍生线程。在sv中,当程序中的initial块全部执行完毕,仿真器就退出了。如果我们希望等待fork块中的所有线程执行完毕再退出结束initial块,我们可以使用wait fork语句来等待所有子线程结束,如下代码:

task run_threads;
    ...
    fork
        check_trans(tr1);//线程1
        check_trans(tr2);//线程2
        check_trans(tr3);//线程3
    join_none
    ...
    //等待所有fork中的线程结束再退出task
    wait fork;
endtask

5、disable ***:停止单个线程。在使用了fork...join_any或者fork...join none以后,我们可以使用disable来指定需要停止的线程。前提是需要给fork的进程设置Tag;

 6、停止多个线程:disable fork,可以停止从当前线程中衍生出来的所有子线程;下图中中disable fork停止了线程1-4,单独保留了线程0;

 7、缺陷使用方法:停止被调用多次的任务,如下图:任务wait_for time_out被调用了三次,从而衍生了三个线程。线程0在#2延时之后禁止了该任务,而由于三个线程均是"同名"线程,因此这些线程都被禁止了,最终也都没有完成。



三、线程间的通信(IPC,Interprocess Communication)1-event

1、Event:事件

        声明:event e1,e2;//声明了两个事件,事件不需要new;

        等待:@e1、@e2,边沿阻塞;

        触发:->e1、->e2,边沿敏感的事件是具有时效性的,即当前event的上升沿过去之后,另外一个进程就接受不到了;

         如上,为了避免产生的事件上升沿过去之后不能被采集到,可以使用电平敏感的触发方式“wait(e1.triggered())”来替代边沿敏感的阻塞预期@e1;但是边沿、电平使用时一定要慎重考虑好;

2、通知和需求:如下图,除了使用下截图中的event方式,还可以通过一个wait一个单bit变量来实现线程之间的同步,最终结果:

car is launched

car is moving

 3、uvm也有event:uvm_event,但是比较老旧,并且没有擦除作用...具体使用方式待补充



四、线程间的通信2-semaphore

1、Semaphore:旗语,可以实现对同一资源的访问控制,比如家庭中只有一辆车,车子每次只能由丈夫或妻子开;对于初学者而言,无论线程之间在共享什么资源,我们都应该避免这种共享,对于硬件和软件都是一样的;最好是在同一个时间段同一个资源只有一个对象可以访问它,就可以通过semaphore实现控制;

2、semaphore有三种基本操作:new()、get()、get();一般情况我们只给一把钥匙,即sem=new(1),new中不传入参数,表示没有钥匙;get、put没有参数时,默认表示的是1把钥匙;eg:get(1)返回0表示没有钥匙,进程阻塞;返回1表示有足够的钥匙,阻塞打开;

3、try_get()函数:如上截图,可以根据try_get()函数的返回值0、1进行下一步操作,返回1表示有足够的钥匙,并且已经给你拿到了一把钥匙,0表示没有足够钥匙,没有拿到钥匙,之后看你需要等还是不等,都是可以的,即这里是不会阻塞的,是0、1都会继续向下执行;但是使用get()的话,如果拿不到的话就会一直阻塞到这里;

4、例子:下例子表示只有一条总线,但是同时有两个sqr激励,我们只希望同时只有一个sqr激励这条总线,这时我们就可以使用semaphore来控制该线程;下面两个到底谁先,0时刻是随机的;如下截图中的两个sqr每次只有一个会被激励;如果在最后不把钥匙put回去,最终这个程序会死锁;

 5、资源共享的要求

6、愚蠢的semaphore:刚开始new(1)了一把钥匙,但是还没get()的时候,我们put(1),这时总共有两个钥匙,所以semaphore其实是非常的笨;所以使用semaphore时一定要注意自己的coding,来避免这类愚蠢问题;



五、线程间的通信3-mailbox

1、Mainbox:信箱,使用方式和对象一样,需要例化,其实就是初始化;其实就是放置一些数组的缓存;

2、除了以上的操作,还有try_put、try_get、try_peek,都是非阻塞的;

3、例子:可以实现线程间的通信;下面的例子中使用了mailbox,队列和数组也可以完成mailbox相应的功能;P线程需要执行了3次,C线程需要执行4次,但是最终C线程只执行了3次,原因是mailbox中的数据被拿空了;P线程是不消耗时间的,但是在0时刻只能将1个数据放进去,后面的两次循环被阻塞,原因是mailbox满了,除非C线程1ns之后执行一次,这时mailbox可以被P线程第二次放入程序;

 4、例子:有如下截图的数据通信的需求;

         如下截图,下面这个列子完成了上面截图的要求:

 如下,使用队列替换mailbox,最终结果和上面一样,注意下面中一个重点ref

 6、fork join_none多任务启动:在不影响主进程的同时并行发起中间的两个进程;

6、mailbox的用法,与fifo的使用很相似,并且我们可以使用队列来代替mailbox;

7、重点:队列传递的时候,如果想要影响队列本身,则在函数声明中一定要用ref;因为如果没有ref,表示的是队列的copy,display内部操作的是拷贝的队列,是控制不到外部队列的元素的pop的;

8、队列和mailbox的异同:队列的空间大小一定是不固定的

9、mailbox的特性:下面第三点如果是int,则说明该mailbox只能存放int型;

10、event:主要解决线程之间的同步、semaphore:主要解决线程之间对同一资源的访问管理、mailbox:主要解决线程之间的通信;三者都可以做同步,如下:

11、三者比较:

问题:UVM中也有fifo(uvm_fifo?),和sv中的fifo(mailbox、fifo)有什么不一样呢?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值