SV语言部分(5)

在这一部分主要讲一下线程的使用、控制及线程间的通信(重点)。

目录

一、线程的使用

1、程序和模块

2、什么是线程

3、线程的澄清

二、线程的控制

 1、fork并行线程语句块

 2、等待所有衍生线程

3、停止单个线程

 4、停止多个线程

 5、停止被多次调用的任务

三、线程间的通信

1、概述

2、event事件

(1)在event的边沿阻塞

(2)等待事件的触发

(3)通知的需求

 3、semaphore旗语

(1)semaphore的操作

 (2)资源共享的需求

4、mailbox信箱 

 (1)mailbox与queue的区别

5、通信要素的比较和应用


一、线程的使用

1、程序和模块

  1. module (模块) 作为SV从Verilog继承过来的概念,自然地保持了它的特点,除了作为RTL模型的外壳包装和实现硬件行为在更高层的集成层面,模块之间也需要通信和同步
  2. 对于硬件的过程块它们之间的通信可理解为不同逻辑/时序块之间的通信或者同步,是通过信号的变化来完成的。
  3. 从硬件实现的角度来看,Verilog通过always,initial过程语句块和信号数据连接实现进程间通信。
  4. 我们可以将不同的module作为独立的程序块,他们之间的同步通过信号的变化 (event触发)、等待特定事件 (时钟周期) 或者时间 (固定延时) 来完成

 如果按照软件的思维理解硬件仿真仿真中的各个模块首先是独立运行的线程(thread)
模块 (线程)在仿真一开始便并行执行,除了每个线程会依照自身内部产生的事件来触发过程语句块之外,也同时依靠相邻模块间的信号变化来完成模块之间的线程同步

2、什么是线程

  1. 线程即独立运行的程序
  2. 线程需要被触发,可以结束或者不结束
  3. 在module中的initial和always,都可以看做独立的线程,它们会在仿真0时刻开始,而选择结束或者不结束。
  4. 硬件模型中由于都是always语句块,所以可以看成是多个独立运行的线程,而这些线程会一直占用仿真资源,因为它们并不会结束。
  5. 软件测试平台中的验证环境都需要由initial语句块去创建,而在仿真过程中验证环境中的对象可以创建和销毁,因此软件测试端的资源占用是动态的

3、线程的澄清

二、线程的控制

 1、fork并行线程语句块

 2、等待所有衍生线程

在SV中,如果我们希望等待fork块中的 所有线程执行完毕再退出结束initial块,使用wait fork来等待所有线程结束。

说明:

  1. 这里说的一般是fork join_any和fork join_none,因为fork join自己就能够全部执行完后才退出
  2. SV,没有wait语句,父线程结束的情况下,并不会被主动回收它自己开辟的子线程

  3. 这些子线程会老老实实的在后台运行,直到运行结束,这些线程被叫做僵尸线程

  4. 凡事用fork join_any/fork join_none开辟的线程,认为它没有必要了之后,一定要disable这些线程(后面的知识)

3、停止单个线程

使用fork join_any/fork join_none开辟的线程,我们可以使用disable来指定需要停止的线程。

 4、停止多个线程

disable fork可以停止从当前线程中衍生出来的所有子线程

 5、停止被多次调用的任务

如果你给某一个任务或者线程指明标号,那么当这个线程被调用多次以后,如果通过disable去禁正这个线程标号所有衍生的同名线程都将被禁止

 

三、线程间的通信

1、概述

  1. 测试平台中的所有线程都需要同步并交换数据
  2. 一个线程需要等待另一个
  3. 多个线程可能同时访问同一个资源。
  4. 线程之间可能需要交换数据
  5. 所有这些数据交换和同步称之为线程间的通信(IPC,Interprocess Communication)

2、event事件

Verilog中,一个线程总是要等待一个带@操作符的事件。这个操作符是边沿敏感的,所以它总是阻塞着、等待事件的变化
其他线程可以通过->操作符来触发事件,结束对第一个线程的阻塞。

说明:event不需要用new函数,仍然可以把它当作一个对象

(1)在event的边沿阻塞

 e1和e2在同一个时刻被触发,但由于delta cycle的时间差,使得两个初始化块可能无法等到e1或者e2。

说明:存在竞争问题


所以,更安全的方式可以使用event的方法triggered()。

(2)等待事件的触发

  1.  可以使用电平敏感的wait (e1.triggered )来替代边沿敏感的阻塞语句@e1
  2. 如果事件在当前时刻已经被触发,则不会引起阻塞。否则,会一直等到事件被触发为止。
  3. 这个方法比起@而言,可以避免在相同时刻触发event而带来的竞争问题但同样无法捕捉已经被触发,但后续才等待的事件。(这一点也是event和wait最大的区别

(3)通知的需求

从这个例子可以看到,尽管在car::drive()中同时启动了两个线程car::launch()和car::move(),然而car::move通过两个线程之间共享的信号car::start来判断什么时候可以行驶,即利用了wait语句完成了线程launch通知线程move. 

如果将上面的公共变量car::start修改为event(事件),那么通过事件的触发和判断事件是否被触发过,也可以实现一样的需求

 上面依然可以通过event的触发,来多次通知另外一个线程。

注意,对于线程多次通知的需求,可以使用@或者wait(event.triggered)。

 3、semaphore旗语

  1. semaphore可以实现对同一资源的访问控制
  2. 对于初学者而言,无论线程之间在共享什么资源,都应该使用semaphore等资源访问控制的手段(通过一些手段,完成访问互斥),以此避免可能出现的问题
  3. semaphore有三种基本操作。new()方法可以创建一个带单个或者多个钥匙的semaphore,使用get()可以获取一个或者多个钥匙,而put()可以返回一个或者多个钥匙(get、put函数都是阻塞的
  4. 如果你试图获取一个semaphore而希望不被阻塞,可以使用try_get()函数(非阻塞 )。它返回1表示有足够多的钥匙(已经帮忙拿了一把钥匙且里面有足够的钥匙),而返回0则表示钥匙不够

说明:get、put 没有参数,默认拿、归还一把钥匙

(1)semaphore的操作

 (2)资源共享的需求

  1. 对于线程间共享资源的使用方式,应该遵循互斥访问(mutexaccess) 原则
  2. 控制共享资源的原因在于,如果不对其访问做控制可能会出现多个线程对同一资源的访问,进而导致不可预期的数据损坏和线程的异常,这种现象称之为"线程不安全”
  3. key在使用前必须要做初始化,即要告诉用户它自带几把钥匙

4、mailbox信箱 

  1. 线程之间如果传递信息,可以使用mailbox。
  2. mailbox和队列queue有相近之处。
  3. mailbox是一种对象,因此也需要使用new()来例化。例化时有一个可选的参数size来限定其存储的最大数量。如果size是0或者没有指定,则信箱是无限大的,可以容纳任意多的条目.
  4. 使用put()可以把数据放入mailbox,使用get()可以从信箱移除数据
  5. 如果信箱为空,则get()会阻塞;如果信箱为满,则put()会阻塞;peek()可以获取对信箱里数据的拷贝而不移除它
  6. 线程之间的同步方法需要注意,哪些是阻塞方法(哪些可能需要等待时间的),哪些是非阻塞方法(即哪些是立即返回的)。

 

 

 

 (1)mailbox与queue的区别

  1. maibox必须通过new()例化,而队列只需要声明
  2. mailbox可以将不同的数据类型同时存储,不过这么做是不建议的(建议存储相同的类型);对于队列来讲,它内部存储的元素类型必须一致
  3. mailbox的存取方法put()和get()是阻塞方法(blocking, method), 即使用它们时,方法不一定会立即返回,队列的存取方法push_back()和pop front()方法是非阻塞的,会立即返回。
  4. 因此在使用queue取数时,需要额外填写 wait(queue.size >0)才可以在其后对非空的queue做取值操作。
  5. 此外,也应该注意,如果要调用阻塞方法,那么只可以在task中调用,因为阻塞方法是耗时的;而调用非阻塞方法,例如queue的push_back()和pop_front(),既可以在task又可以function中调用。
  6. mailbox只能够用作FIFO,而queue除了按照FIFO使用,还有其它应用的方式例如LIFO (Last In First Out)
  7. 对于mailbox变量的操作,在传递形式参数时,实际传递并拷贝的是mailbox的指针
  8. 在第二个例子中的task_display(),关于queue的形式参数声明是ref方向因为如果采用默认的input方向,那么传递过程中发生的是数组的拷贝,以至于方法内部对queue的操作并不会影响外部的queue本身。因此在传递数组时,读者需要考虑,对数组做的是引用还是拷贝,进而考虑端口声明的方向

5、通信要素的比较和应用

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值