一.线程

1.定义

独立运行的程序。

2.特性

(1)每个always过程块和initial过程块都是相互独立的线程;
(2)每个线程呈现树状结构,父线程可以开辟多个子线程;
例子:

fork
	begin
		$display("Block one\n");
		# 20;
	end 
	begin
		$display("Block two\n");
		@eventA;
	end
join

解释:fork join内部包含两个并行运行的begin end块,即fork join内部包含两个线程;

(3)线程可以持续运行(如always过程块),也可以提前结束(如initial过程块);

3.分类

(1)串行线程
语法:begin end
特点:串行执行
(2)并行线程
语法:fork join, fork join_any, fork join_none
特点:并行执行

二.并行线程

(1)详述

链接: link

(2)例子:线程树

代码:

module tb;
    task automatic exec(int id,int t);
        $display("%0t exec[%0d] entered",$time,id);
        #(t*1ns);
        $display("%0t exec[%0d] exited",$time,id);
    endtask 
    initial begin:proc1
        exec(1,25);
    end
    initial begin:proc2
        fork 
            exec(2,10);
            exec(3,20);
        join_any
        fork 
            exec(4,30);
            exec(5,40);
        join_none

    end
endmodule

线程示意图:
在这里插入图片描述
结果:
在这里插入图片描述

三.线程的结束

(1)wait fork
(2)disable fork
待补充

四.线程的控制(时序控制)

(1)简介

sysytemverilog可以通过使用事件触发、延时和等待实现时序控制;

(2)具体方法

(a) 事件触发

语法:

@(触发事件) 执行代码;

例子:

@(data1)  out = in;
@(posedge clk) out = in;

(b) 延时

语法:

#(延迟时间) 执行代码;

例子:

#10 out = in;

(c) 等待

语法:

wait(条件) 执行代码;

例子:

wait(data.size>0) out = in;

注意:
等待也可以通过事件触发的形式实现;

五.线程的同步与通信

1.概述

目的:线程之间需要进行同步和交换数据;

2.事件

(1)概述

定义:
事件就是一个信号标志,多个线程可以等待这个信号,当信号被触发,等待这个时间的线程就可以继续执行。
作用:
协调并行线程的操作顺序,保证某些线程在其他操作完成后才开始执行。

(2)语法

//定义
event 事件名;
//触发事件
-> 事件名;
//等待事件触发
@事件名;
wait(事件名.triggered);

(3)注意

(a)event声明的是句柄(地址);
(b)事件触发是一个瞬时脉冲;
(c)@和wait
因为事件触发是一个脉冲信号,所以等待边沿触发**@和等待电平触发wait**是等效的;

(4)例子

(a)event实现的例子
代码:

module tb1;
    
    task automatic wait_event(event e,input string name);
        $display("@%0t Start waiting event %s",$time,name);
        @e;
        //wait(e.triggered);
        $display("@%0t Finish waiting event %s",$time,name);
    endtask
    
    event e1,e2,e3;
    initial begin
        fork 
            wait_event(e1,"e1");
            wait_event(e2,"e2");
            wait_event(e3,"e3");
        join
    end
    initial begin
        fork
            begin #10 ->e1; end
            begin #20 ->e2; end
            begin #30 ->e3; end
        join
    end
endmodule

结果:
在这里插入图片描述
(b)@(事件名)和wait(事件名)的相同
i 情况一:在不同时刻,先等待事件,后触发事件,事件均能被捕捉到;
代码:

module tb3;
    event e1,e2;
    initial begin
        #5;
        $display("@%t:Start waititng event %s",$time,"e1");
        @(e1);
        $display("@%t:Finish waiting event %s",$time,"e1");
    end
    initial begin
        #5;
        $display("@%t:Start waititng event %s",$time,"e2");
        wait(e2);
        $display("@%t:Finish waiting event %s",$time,"e2");
    end
    initial begin
        #10;
        $display("@%t:Start trig event %s",$time,"e1");
        -> e1;
        $display("@%t:Finish trig event %s",$time,"e1");
    end
    initial begin
        #10;
        $display("@%t:Start trig event %s",$time,"e2");
        -> e2;
        $display("@%t:Finish trig event %s",$time,"e2");
    end
endmodule

结果:
在这里插入图片描述
说明:
事件均在第5个时间单位开始等待触发,而事件均在第10个时间单位进行触发,事件均被捕捉到;
ii 情况二:在不同时刻,先触发事件,后等待事件,事件均不能被捕捉到;

module tb3;
    event e1,e2;
    initial begin
        #5;
        $display("@%t:Start trig event %s",$time,"e1");
        -> e1;
        $display("@%t:Finish trig event %s",$time,"e1");
    end
    initial begin
        #5;
        $display("@%t:Start trig event %s",$time,"e2");
        -> e2;
        $display("@%t:Finish trig event %s",$time,"e2");
    end
    initial begin
        #10;
        $display("@%t:Start waititng event %s",$time,"e1");
        @(e1);
        $display("@%t:Finish waiting event %s",$time,"e1");
    end
    initial begin
        #10;
        $display("@%t:Start waititng event %s",$time,"e2");
        wait(e2);
        $display("@%t:Finish waiting event %s",$time,"e2");
    end

endmodule

结果:
在这里插入图片描述
说明:
事件均在第5个时间单位进行触发,而事件均在第10个时间单位才开始等待,事件均未被捕捉到;
iii 情况三:在同一时刻,先等待事件,后触发事件,事件均能被捕捉到;
代码:

module tb3;
    event e1,e2;
    initial begin
        //#10;
        $display("@%t:Start waititng event %s",$time,"e1");
        @(e1);
        $display("@%t:Finish waiting event %s",$time,"e1");
    end
    initial begin
        //#10;
        $display("@%t:Start waititng event %s",$time,"e2");
        wait(e2);
        $display("@%t:Finish waiting event %s",$time,"e2");
    end
    initial begin
        //#5;
        $display("@%t:Start trig event %s",$time,"e1");
        -> e1;
        $display("@%t:Finish trig event %s",$time,"e1");
    end
    initial begin
        //#5;
        $display("@%t:Start trig event %s",$time,"e2");
        -> e2;
        $display("@%t:Finish trig event %s",$time,"e2");
    end
endmodule

结果:
在这里插入图片描述
说明:
事件均在第0个时间单位等待触发和进行触发,且等待触发过程块在进行触发过程块之前,事件均被捕捉到;
iv 情况四:在同一时刻,先触发事件,后等待事件,事件均未被捕捉到;
代码:

module tb3;
    event e1,e2;
    initial begin
        //#5;
        $display("@%t:Start trig event %s",$time,"e1");
        -> e1;
        $display("@%t:Finish trig event %s",$time,"e1");
    end
    initial begin
        //#5;
        $display("@%t:Start trig event %s",$time,"e2");
        -> e2;
        $display("@%t:Finish trig event %s",$time,"e2");
    end
    initial begin
        //#10;
        $display("@%t:Start waititng event %s",$time,"e1");
        @(e1);
        $display("@%t:Finish waiting event %s",$time,"e1");
    end
    initial begin
        //#10;
        $display("@%t:Start waititng event %s",$time,"e2");
        wait(e2);
        $display("@%t:Finish waiting event %s",$time,"e2");
    end
endmodule

结果:
在这里插入图片描述
说明:
事件均在第0个时间单位等待触发和进行触发,且进行触发过程块在等待触发过程块之前,事件均未被捕捉到;

(c)@(事件名)和wait(事件名.triggered)的不同
上述情况四(在同一时刻,先触发事件,后等待事件),wait(事件名.triggered)能够捕捉到事件;
代码:

module tb3;
    event e1,e2;
    initial begin
        //#5;
        $display("@%t:Start trig event %s",$time,"e1");
        -> e1;
        $display("@%t:Finish trig event %s",$time,"e1");
    end
    initial begin
        //#5;
        $display("@%t:Start trig event %s",$time,"e2");
        -> e2;
        $display("@%t:Finish trig event %s",$time,"e2");
    end
    initial begin
        //#10;
        $display("@%t:Start waititng event %s",$time,"e1");
        @(e1);
        $display("@%t:Finish waiting event %s",$time,"e1");
    end
    initial begin
        //#10;
        $display("@%t:Start waititng event %s",$time,"e2");
        wait(e2.triggered);
        $display("@%t:Finish waiting event %s",$time,"e2");
    end
endmodule

结果:
在这里插入图片描述
说明:
事件均在第0个时间单位等待触发和进行触发,且进行触发过程块在等待触发过程块之前,wait(事件名.triggered)能捕捉到事件;

3.旗语(semaphore)

(1)作用

实现对共享资源的访问控制;

(2)说明

通俗举例:
一个资源必须通过钥匙才能访问,这个资源配了多把钥匙(比如3把,旗语名.new(3)),只要有其中的任意2把(也可以设置为其他值)就可以访问;

第一个人先拿走了两把钥匙(旗语名.get(2))对该资源进行访问,而第二个人也想访问该资源就请求获取两把钥匙(旗语名.get(2)),此时只剩下了一把闲余钥匙,所以他就需要一直等待第一个人至少归还1把钥匙(旗语名.put(1))以后才能访问该资资源;

但是当第二个人请求获取两把钥匙,可是闲余钥匙数小于需求数,此时不希望第二个人一直等着(这个人可以去干别的事),所以他可以通过使用旗语名.try_get(钥匙数),该方法会有一个返回值告诉这个人是否成功取到钥匙(取到返回1,没有取到返回0);

(3)语法

//声明
semaphore 旗语名;
//实例化
旗语名 = new(钥匙数(默认值为0));
//阻塞式获取钥匙
旗语名.get(获取钥匙数(默认值为1));
//非阻塞获取钥匙
旗语名.try_get(获取钥匙数(默认值为1));
//放入钥匙
旗语名.put(放入钥匙数(默认值为1));

(4)例子

(a)旗语的基本操作
代码:

module tb4;
    semaphore sem;
    initial begin
        sem = new(3);
    end
    initial begin
        #10;
        $display("@time:%0t:A start  get key",$time);
        sem.get(2);
        $display("@time:%0t:A finish get key",$time);
        #20;
        $display("@time:%0t:A start put key",$time);
        sem.put(2);
        $display("@time:%0t:A finish put key",$time);
    end
    initial begin
        #20;
        $display("@time:%0t:B start get key",$time);
        sem.get(2);
        $display("@time:%0t:B finidh get key",$time);
    end
endmodule

结果:
在这里插入图片描述
(b)try_get
代码:

module tb5;
    semaphore sem;
    initial begin
        sem = new(3);
    end
    initial begin
        #10;
        $display("@time:%0t: start get key",$time);
        if(sem.try_get(4))
            $display("@time:%0t:Success get key",$time);
        else 
            $display("@time:%0t:Fialed get key",$time);
    end
endmodule

结果:
在这里插入图片描述

(5)注意

(a)取钥匙可以请求取任意把数(get/try_get),只是取得过多很容易取失败;
(b)放入钥匙可以放入任意把钥匙,甚至可以超过取的把数,也可以超过最开始该资源配备的把数;
(c)旗语声明的也是句柄;

4.信箱

(1)作用

实现线程间的通信,相当于一个fifo队列;

(2)语法

//声明
mailbox 信箱名; 					//不限定成员变量类型
mailbox #(成员变量类型) 信箱名;	//限定成员变量类型
//实例化
信箱名 = new(信箱大小(默认值为0,默认值表示不限定信箱大小));
//阻塞式写入、取出、读出数据
信箱名.put(写入数据);
信箱名.get(取出数据保存变量名);
信箱名.peek(读出数据保存变量名);
//非阻塞式写入、取出、读出数据
信箱名.try_put(写入数据);			//有返回值,操作成功为1,操作失败为0
信箱名.try_get(取出数据保存变量名);	//有返回值,操作成功为1,操作失败为0
信箱名.try_peek(读出数据保存变量名);	//有返回值,操作成功为1,操作失败为0
//获取信箱内数据个数
信箱名.num(写入数据);

(3)例子

代码:

module tb6;
    mailbox #(int) mai;
    int put_data;
    int data_peek;
    int data_get;
    initial begin
        mai = new(10);
        repeat(10)begin
            put_data = $random();
            mai.put(put_data);
            $display("@time:%0t put data is %0d",$time,put_data);
        end
    end
    initial begin
        #5;
        repeat(3)begin
            mai.peek(data_peek);
            $display("@time:%0t peek data is %0d",$time,data_peek);
        end
    end
    initial begin
        #10;
        repeat(5)begin
            mai.get(data_get);
            $display("@time:%0t get data is %0d",$time,data_get);
        end
    end
    initial begin
        #10;
        $display("@time:%0t data num is %0d",$time,mai.num());
    end
endmodule

结果:
在这里插入图片描述

(4)注意

信箱声明的也是句柄。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐