SystemVerilog学习杂记——线程控制和通信

线程控制

fork…join、fork…join_any、fork…join_none

        开启并行块

        fork…join块中的程序并行执行,每个并行执行的语句为一个线程,待所有程序(线程)执行完后退出该块;

        fork…join_any块中的程序并行执行,每个并行执行的语句为一个线程,其中一个程序(线程)执行完便退出该块,执行下一程序,块中其他未执行完的程序继续执行;

        fork…join_none块中的程序并行执行,每个并行执行的语句为一个线程,块中所有程序都不等待,直接进入下一程序;(开启了块中的线程,但不等待)

wait fork

        等待正在执行的线程执行完成。        

        在使用fork…join_any、fork…join_none时由于没有等待其中某些线程执行完成,可能出现所有initial块执行完毕,仿真器退出,但fork…join_any或fork…join_none块中的某个线程还在执行的情况,这时,伴随initial块的结束会关闭这些正在执行的线程,如果我们想等这些正在执行的线程执行完毕在退出,就可以使用wait fork。

initial begin

......

fork
... ...
join_none

wait fork;//等待线程执行完
end

disable

         停止某一线程。

task name

......

fork : mytime
#10  $display("10");  //线程1
#50  $display("50");  //线程2 
#80  $display("80");  //线程3 
join_any

disable mytime;  //停止mytime中的线程
//disable name;

endtask

        注意,若某一指明标号的线程被多次调用(如上面代码中的mytime被多次调用),在使用disable将其关闭,则所有的同名线程都会关闭。

        disable同样可以用来关闭task,例如上面代码中的任务name,可以用disable name来关闭。

线程通信

event  事件

        触发操作符:->

        等待触发:@

        等待触发:@,边沿敏感,等待一个触发作符来触发该事件,然后继续执行。触发之前这个线程是阻塞的,处于等待状态。每次触发事件时,@都能捕捉到。

event el, e2;
initial begin
    $display("@%0t: 1: before trigger"Stime) ;
    -> e1;    //触发事件e1
    @e2;      //等待触发事件2
    $display("@%0t: 1: after trigger",Stime) ;
end

initial begin
    $display("@%0t: 2: before trigger"Stime);
    -> e2;    //触发事件e2
    @e1;      //等待触发事件1
    $display("@%0t: 2: after trigger",Stime)
end

运行结果:
@0: 1: before trigger
@0: 2: before trigger
@0: 1: after trigger

         $display("@%0t: 2: after trigger",Stime)未执行,这是因为触发e1时(->e1),还未执行等待指令(@e1),等到处于等待状态时(@e1),触发e1(->1)已经执行过了,虽然这个时间很短(可能是一个deita time),但@为边沿敏感,还是被跳过了,更安全的方式是使用triggered()。

         等待触发:triggered()

        triggered(),电平敏感。常见格式:wait(event.triggered());

event el, e2;
initial begin
    $display("@%0t: 1: before trigger"Stime) ;
    -> e1;                     //触发事件e1
    wait(e2.triggered());      //等待触发事件2
    $display("@%0t: 1: after trigger",Stime) ;
end

initial begin
    $display("@%0t: 2: before trigger"Stime);
    -> e2;                     //触发事件e2
    wait(e1.triggered());      //等待触发事件1
    $display("@%0t: 2: after trigger",Stime)
end

运行结果:
@0: 1: before trigger
@0: 2: before trigger
@0: 1: after trigger
@0: 2: after trigger

         注意,triggered()的局限在于,只要事件触发过一次,在调用这个函数时就一直返回的是1,所以wait(event.triggered())这种等待方式不适用于事件会多次触发的情况,反复触发用@。

semaphore  旗语

        semaphore可以实现对同一资源的访问控制

        对于初学者而言,无论线程之间在共享什么资源,都应该使用semaphore等资源访问控制的手段,以此避免可能出现的问题

        semaphore有三种基本操作。new()方法可以创建一个带单个或者多个钥匙的semaphore使用get()可以获取一个或者多个钥匙(阻塞),而put()可以返回一个或者多个钥匙

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

program automatic test (bus ifc.TB bus)
semaphore sem;          // 创建一个semaphore
initial begin
    sem = new(1);       // 分配一个钥匙,不传参则不分配钥匙
    fork
        sequencer() ;   // 产生两个总线事务线程,两个线程都会占用总线
        sequencer()
    join
end
task sequencer ;
    repeat($urandom%10) // 随机等待0-9个周期
    @bus.cb ;
    sendTrans () ;      // 执行总线事务
endtask
task sendTrans ;
    sem.get(1);         // 获取总线钥匙,若获取失败则阻塞,等待获取成功
    @bus.cb;            // 把信号驱动到总线上
    bus.cb.addr <= t.addr ;
    sem.put(1);         // 处理完成时把钥匙返回
endtask
endprogram

        上面代码中的钥匙可以理解为访问总线的权限。

        注意get()、put()相当于钥匙减一和加一,当semaphore中没有钥匙时执行get会阻塞,但是,当你本身没有执行get(),直接执行put()时,钥匙会加一,这在逻辑上是不通的,但SV中允许这样做,编译器不会报错,这样可能会导致控制的逻辑与预期不符,使 semaphore不能达到控制互斥访问的目的,当然,特殊情况下也可以这样做。

mailbox

        用于线程之间传递信息。

        mailbox是一种对象,需要使用new()来例化。例化时ne w中传递的参数new(size)来限定其存储的最大数量。如果size是0或者没有指定,则信箱是无限大的,可以容纳任意多的条目。

        mailbox.put(a),将a放入信箱中;mailbox.get(b),从信箱中取出一个信息存入b,取出的信息从mailbox中去除;mailbox,peek(c),获取信箱里数据的拷贝而不移除它,将信息存入c。

        如果信箱为满,则put()会阻塞,等到信箱非满; 如果信箱为空,则get()和peek()会阻塞,等到信箱非空。对应的也有非阻塞方法try_put()try_get()try_peek()

        在声明时可显式的确定信箱中元素的类型,格式为mailbox #(Type = T),如:mailbox #(int) mb_a;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值