线程以及线程间的通信 | Systemverilog 笔记 6

Posted by Kion on January 21, 2020

创建线程的方法

fork …join_none以及fork…join_any。两者的区别在于,前者在调度块内语句时,父线程继续执行,且父线程的后续语句早于fork内线程;后者则在块内任一个语句完成后。线程内的语句是并行的。而传统的fork…join则需要等到块内所有语句执行完成后才进行后续语句执行。

停止线程的方法

使用disable forklabel来停止线程。若没有指明线程标记,则会停止当前线程衍生出的所有子线程。若停止某个的任务的调用,那么其他调用该任务的线程也会停止。

线程间的通讯

可以使用事件、旗语、信箱、@事件控制以及wait语句。

旗语

可以利用旗语实现对同一资源的访问管理。

program automatic test(bus_ifc.TB bus);
    semaphore sem;/*创建旗语*/
    initial begin
        sem = new(1);/*分配钥匙数目*/
        fork
            sequencer();
            sequencer();
        join
    end

    task sequencer;
        repeat($urandom%10)
            @(bus.cb);
        sendTrans();
    endtask
    task sendTrans;
        sem.get(1);/*获取钥匙*/
        @(bus.cb);
        bus.cb.addr<=t.addr;
        ...
        sem.put(1);/*返回钥匙*/
    endtask
endprogram
信箱

先进先出的一个存储器。可以用put()和get()语句进行取放。不过要注意取放的先后,无法对满的信箱放,无法对空的信箱取。peek()可以拷贝数据而不移除。所以为了保证取放步调一致,可以采用peek()、事件、额外信箱。

program automatic mbx_evt;
    mailbox mbx; /*信箱*/
    event handshake; /*控制取放同步的事件*/
    class Producer; /*放类*/
        task run;
            for(int i=0; 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:Consumer
    
    Producer p;
    Consumer c;
    initial begin
        mbx = new();
        p = new();
        c = new();
        fork /*取放并行启动*/
            p.run();
            c.run();
        join
    end
endprogram