1.线程的通信
线程之间的通信包含3点:event、旗语、信箱。
2.event事件
(1)Verilog中,一个线程总是要等待一个带@操作符的事件。这个操作符是边沿敏感的,所以它总是阻塞着、等待事件的变化。
(2)其它线程可以通过->操作符来触发事件,结束对第一个线程的阻塞。
(3)event可看作类,只是不需要new()函数。
(4)在event的边沿阻塞:@,上升沿或是下降沿都包括,所以需要指定是下降沿还是上升沿的事件。
(5)将(4)中的等待边沿触发@换成等待电平触发
-
triggered函数用于检查一个事件是否被触发过,返回值是一个状态;
-
wait(event.triggered):如果在当前的仿真时间范围内,事件曾触发过,语句不会被阻塞,否则, wait(event.triggered)会一直等待事件被触发。
(6)例题
下面对于event的特性说明哪些是正确的。
3.通知的需求(event)
(1)例子1
(2)例子2
对比例子1和2,通过共享信号来实现线程的通信的方式,也可以通过event的方式来实现。
(3)多次通知时:@和wait的对比
4.旗语:semaphore
(1)概述
(2)semaphore的操作示例
-
$urandom: 产生32位无符号数;
-
semaphore须用new(n)创建,n为几把钥匙;
-
sequencer相当于发生器,等待@bus.cb事件;
(3)下面对于semaphore的描述哪些是正确的
5.资源共享的需求(semaphore)
(1)概述
-
对于线程间共享资源的使用方式,应该遵循互压访问(mutexaccess)原则。
-
控制共享资源的原因在于,如果不对其访问做控制,可能会出现多个线程对同一资源的访问,进而导致不可预期的数据损坏和线程的异常,这种现象称之为“线程不安全"。
-
还是以这辆BYD为主角,如果丈夫和妻子都要开这辆车,而这辆车只有一把钥匙的话,只能以一定的顺序先后使用,才可以解决开车的问题吧。
(2)例一
(3)例一的分析
(4)例一的问题与改进
-
若一开始没有钥匙,没法造出一把钥匙。
-
初始化一把钥匙,如果误操作未取却还了一把钥匙,现在剩两把钥匙。
-
丈夫拿了钥匙,妻子却仍可以归还钥匙。
(5)改进后的例子分析
6.mailbox信箱
(1)简述
(2)使用mailbox通信
由于信箱容量只为1,所以每次必须等取出才能再写入,本身producer可以在0时刻进行3次循环放入数据。
7.数据通信的需求
(1)对于一辆车子而言,如果要实时显示这辆车子的状态,会需要多个仪表。显示的参数包括车速、油量、发动机的转速和温度等,这些参数涉及到了各个传感器到汽车控制中枢的通信。
如果我们继续通过上面这辆BYD,来模拟不同传感器(线程)到车的中央显示的通信,可以利用SV的mailbox (信箱)来满足多个线程之间的数据通信。
(2)例子
注意点:forever的使用:一直会运行;#sample_period:采样时间间隔;std::randomize:std从哪来的可参考4_1-1,std是预定义的标准库。
(3)对于mailbox的用法,与FIFO的使用很相似。如果我们将上面·的mailbox用队列来替代的话,则可以修改为下面的例码。
-
队列不需要new()。
-
当没有wait(q.size()>0); val会报错。
-
ref的使用:在队列作为形参的传递时,对形参的修改可以影响实际的队列。
(4)mailbox与queue的区别
(5)例题
8.三种方式与进程同步问题
(1)背景
有时进程之间也需要同步。这里我们来考虑,如果要对这辆车熄火(stall)的话,得先检查是否车挂挡在P (park) ,进而再将车钥匙拔出。我们可以将stall和park两个线程的同步视作,先由stall发起同步请求,再等待park线程完成并响应同步请求,最后由stall线程继续其余的程序,最终结束熄火的过程。
(2)event同步
(3)semaphore同步
由于key的值一开始为0;所以key.get()得不到钥匙,put()先执行。
(4)mailbox同步
(5)例题结果
(6)三种方式的分析