zoukankan      html  css  js  c++  java
  • 07-SV线程以及线程间的通信

    1、几种语句块的区别

      (1)fork……join:块内语句以并发方式执行

      (2)begin……end:块内语句以顺序方式执行

      (3)fork……join_none:其块内语句执行时,父线程继续执行

      (4)fork……join_any:其块内第一个语句完成后,父线程才继续执行,其他停顿的线程也得以继续

      在并发线程中务必使用自动变量来保存数值

    2、停止线程

      (1)使用disable语句可以用于停止SV中的线程

      (2)使用disable fork语句能够停止从当前线程中衍生出来的所有子线程

    3、线程间的通信

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

      (2)边沿敏感的@操作符用来阻塞线程,->操作符用来触发线程,电平敏感的wait(e1.triggered())不会引起阻塞

      (3)传递事件:SV中的事件可以像参数一样传递给子程序

      (4)等待多个事件:wait fork 或者使用线程计数(例7.27-29)

    4、旗语(semaphore)

      使用旗语可以实现对同一资源的访问控制。当测试平台中存在一个资源,如一条总线,对应着多个请求方,而实际的物理设计中又只允许单一驱动时,便可以使用旗语。在SV中,一个线程如果请求控制权而得不到,则会一直阻塞,多个阻塞的线程会以先进先出的方式进行排队。

      (1)旗语的三种基本操作:new方法可以创建一个带单个或多个钥匙的旗语,get可以获取一个或多个钥匙,put则可以返回一个或多个钥匙。如果试图获取一个旗语而希望不被阻塞,可以使用try_get()函数,它返回1表示有足够多的钥匙,返回0表示钥匙不够。

    5、信箱(mailbox)

      (1)信箱可以看成是一个具有源端和收端的FIFO,源端把数据放进信箱,收端则从信箱中获取数据。当源端线程试图向一个容量固定并且已经饱和的信箱里放入数值时,会发生阻塞;当收端线程试图从一个空信箱里移走数据,也会发生阻塞。

      (2)信箱是一种对象,调用new函数来进行实例化,例化时有一个可选的参数size指定信箱的大小,缺省情况下容量不限。

      (3)信箱的基本操作

        put任务把数据放入信箱,get任务则可以移除数据。如果信箱为满,put会阻塞;如果信箱为空,get会阻塞.如果不希望在访问信箱时出现阻塞,可以使用try_get()和try_peek()函数,如果函数执行成功,它们返回非零值,否则返回0.

        get 是从邮箱中移出数据,peek(窥视)则探视信箱里的数据但是不把它移出

      (4)定容信箱在两个线程之间扮演了一个缓冲器的角色(例7.37)

      (5)实现线程同步的几种方式

        定容信箱加探视(peek):消费方使用peek()来探视信箱里的数据而不将其移出,当消费方处理完数据后,便使用get()移出数据。

        信箱加事件:在生产方把数据放入信箱后使用事件来阻塞它,等消费方处理完数据后再触发事件。

        使用两个信箱:一个信箱传递数据,另外一个信箱把消费方的完成信息发回给生产方。

        使用变量或旗语

    6、结论

      设计可以用很多并发运行的独立块来建模,所以测试平台也必须能够产生很多激励流并检验并发线程的反应。所以这些都被组织在一个层次化的测试平台中,并在顶层环境里得到统一。SV在标准的fork……join之外,引入了诸如fork……join_none和fork……join_any这些用于动态创建线程的功能强大的结构。线程间可以使用事件、旗语、信箱,以及经典的@事件控制和wait语句来实现通信和同步。

    7、程序示例

    (1)事件

    program test7;
        event e1,e2;
        initial begin
            $display("@%0t:1:before triggered",$time);
            ->e1;
            //wait(e2.triggered());
            @(e2);
            $display("@%0t:1:after triggered",$time);
        end
    
        initial begin
            $display("@%0t:2:before triggered",$time);
            ->e2;
            //wait(e1.triggered());
            @(e1);
            $display("@%0t:2:after triggered",$time);
        end
    
    
    
    endprogram:test7

    输出:

    # @0:1:before triggered
    # @0:2:before triggered
    # @0:1:after triggered

    (2)定容信箱

    program test7_2;
        //定容信箱
        initial begin
            mailbox mbx;
            mbx = new(1);   //容量为1
            $display("====== Constant volume mailbox =====");
            fork
                    //生产方线程
                    for (int i=1;i<4;i++) begin
                            $display("Producer:before put (%0d)",i);
                            mbx.put(i);
                            //$display("Producer:after put (%0d)",i);
                    end
                    //消费者线程
                    repeat(4) begin
                        int j;
                        #1ns mbx.get(j);
                        $display("Consumer:after get (%0d)",j);
                    end
            join
            
    
        end
    endprogram

    输出:

    # ====== Constant volume mailbox =====
    # Producer:before put (1)
    # Producer:before put (2)
    # Consumer:after get (1)
    # Producer:before put (3)
    # Consumer:after get (2)
    # Consumer:after get (3)

    (3)使用信箱和事件来实现线程的同步

    program automatic mbx_evt;
        mailbox mbx;
        event handshake;
        class Producer;
            task run;
                    for (int i=1;i<4;i++) begin
                        $display("Producer:before put (%0d)",i);
                        mbx.put(i);
                        @handshake;  //用事件阻塞
                        $display("Producer:after put (%0d)",i);
                    end
            endtask
        endclass
    
        class Consumer;
                task run;
                    int i;
                    repeat (3) begin
                        mbx.get(i);
                        $display("Consumer:after get (%0d)",i);
                        ->handshake;  //取出数据之后触发事件 
                    end
                endtask
        endclass
    
        Producer p;
        Consumer c;
        initial begin
            mbx=new();
            p=new();
            c=new();
            //使生产和消费并发运行
            fork
                p.run();
                c.run();
            join
    
        end
    
    endprogram

    输出:

    # Producer:before put (1)
    # Consumer:after get (1)
    # Producer:after put (1)
    # Producer:before put (2)
    # Consumer:after get (2)
    # Producer:after put (2)
    # Producer:before put (3)
    # Consumer:after get (3)
    # Producer:after put (3)
  • 相关阅读:
    QEMU编译及使用方法
    C++中的算法
    C++继承
    gcc savetemps选项
    C++ overload、override、overwrite
    拷贝构造函数与拷贝赋值
    C++中的顺序容器
    C++中的虚函数(1)
    C++中lambda的实现(1)
    正确的时间做适合的事
  • 原文地址:https://www.cnblogs.com/wt-seu/p/12291727.html
Copyright © 2011-2022 走看看