AHB-SRAM项目之并发进程与进程通信(SystemVerilog笔记之4)

在开始该模块之前,应该了解的内容如下:

Fork_join三兄弟,automatic的task和function和静态的有何区别?

开始:

首先将program改成automatic类型,这样program里写taskfunction时,就会让taskfunction变成automatic类型的。

之后用fork join实现generator和driver的启动。

虽然用fork_join将其包起来了,但目前仍旧存在一定的问题,比如:

但现在代码看起来不够简洁,比如u_ahb_master_driver.vif = env_ahbif; 这句将实际的接口与虚接口对应的句柄连接起来这个步骤可以放入new()中进行。所以可以在driver中声明new()方法,这样在new出driver的对象时,就会自动包含此功能。标准化的代码实际也是这样做的。

注意此处的this关键字,指的是class内的virtual interface类型的句柄,用以区分new function的形式参数。之后在env中将接口传递到new函数中就可以了。

可以将fork join中的内容包在task里,由于programautomatic类型的,声明在其中的functiontask块也是automatic型的,因此task在调用的时候作为一个子进程会占用内存,当调用完成以后不会占用内存了。

知识点:静态变量和动态变量之比较。主要是两个变量的存储位置不同,生命周期不同,以及作用域的不同,具体可以看我的sv笔记。

同时,也将new出来的两个对象由静态转为动态(放入动态的task中即可),由于动态变量的声明周期比较短,在task结束后该变量失效

完成该步骤后env中的代码仍然不够简洁,因此要将env中generator生成数据这一块的内容放到generator里面去实现。放到generator中的task run函数中去即可。(之前之所以没有这样做,原因很简单,因为generator和master之间要实现数据的传递,这可以靠进程的通信实现)

在env中调用时,由于还不会mailbox,所以需要一个参数给到diver,可以把driver的参数直接给到generator的run的参数里。

因此,在generator中定义run时,需要一个指针,把产生的激励给到driver的que_q中。这里有两种思路,一是generator的run有一个返回值,将返回值传到driver内。但这样做无异于又多了一个中转,同时还多了传递给driver这一步。

二是将driver包在generator里,让generator有能力对driver进行操作。

这就要求generator的属性列表中有driver类型的句柄(generator指向driver)。

有了driver的属性,gen的函数自然可以对其进行操作了。

这样,目前传递信息这个步骤就在generator中得以实现。后面学了进程的通信后,将会将这个步骤放到mailbox里,两个进程互不打扰。

原则上,我们希望run()函数不要有过多的参数,因此可以把这个参数做到new()里面去。

还是同样的道理,在new一个gen对象的时候,自然有一个driver类型的属性(数据)在gen的类中,run函数(对数据的操作)中需要用到driver类型的数据。因此new函数提供了一个功能:将driver产生的obj通过new的形式参数传递给到了gen的内部。Gen中run实现了对其的操作。可以视为gennerator包住了driver。因此,这两个实例化的顺序需要相反一下。

总结:需要将一个类中的句柄传递给另一个类的属性,让其能够对该类进行操作的时候,我们可以把句柄传递这样一个动作通过new()来实现,有利于实现代码的整洁性,同样,virtual型的interface也可以通过new函数来生成。今后的实验中要熟悉这种代码方式。

这个地方是告诉编译器,把它先当成一个class,可能还未编译driver,但也可以让编译通过。

这样可以解决一个问题,就是两个类相互依赖时,先编译谁都会报错,那么就先这样做让编译通过。

 

进程之间的同步、资源共享和信息传递可以通过事件(event)、旗语(semaphore)和邮箱(mailbox)来实现。

要实现generator和mailbox之间的通信,就需要将mailbox的实例化放入到两者其一中内,或者将mailbox放到两者的外面。

在gen中定义只能传递固定类型的mailbox,将类型放在mailbox外面用#()来声明mailbox要传递的数据类型。

自定义new函数,由于mailbox放在了generator里,所以在new generator的时候顺便把mailbox也new出来。

Driver的驱动部分,可以将产生的数据放到gen2drv这个mailbox里面。

但原本代码中的何时控制generator停止的部分便不能够使用了。

之后在driver中同样定义mailbox,两者的指针要相同。因为已经在new generator时候实例化了mailbox,所以无需在driver中实例化mailbox。

还是和之前的操作大同小异,在class内建立一个mailbox,句柄要和gen中的一样都是gen2drv,然后在new函数中完成句柄指向的操作,new函数中的形式参数为外部需要传递给drv内部的参数。

由于此时driver内的que_q没有句柄传递进来,要靠mailbox 的get函数get到 generator放入到其中的信息,因此写一个并发进程专门用来get data。

注意get的用法,没有返回值。这样源代码不用更改就实现了gen到drv的数据包传递。

到此为止实现了mailbox的相关功能。

然后在env里面将driver和mailbox连接起来。

注意例化的先后,因为mailbox在gen里面,要先例化gen(当然也可以吧mailbox做在gen和drv的外面,然后在gen里面的new函数中将外面的mailbox传递过来就行了)

现在看来env非常整洁,整体program是automatic形式的,因此task是automatic形式的,run执行完以后自动释放内存空间,并且实例化的对象也是automatic形式的,用完自动释放内存。两个组件gen和drv之间通过mailbox实现了data的传递,两个组件互相不包含对方的内容,越顶层的代码越抽象和整洁。

但此时有一个问题,gen中判断何时停止发送数据不能执行,因为gen中不再包含driver型的句柄了,显然drv.counter就无法在gen中应用。就无法判断了何时停止,因此可以通过event对进程进行同步(加一个握手信号)。

如上图所示,在driver中启动一个并发线程,添加一个握手信号。使用driver中que_q中的数量(这个数量>0应该存在一点问题,可能会导致中间出现size()为0时还要等一个时钟周期从而把slex信号拉低)判断是否触发handshake,如果q中有数据,则不必发送。如果q中没有数据,则可以触发handshake,在gen中等待handshake的触发信号到来,一旦q中无数据,则发送一个到mailbox里。

注意,在gen和driver中添加了事件event以后,要将两个事件合并才能够指向同一个event。

同时还应该注意在握手事件里,同样要有推进仿真时间的语句,不然仿真不会像mailbox的get一样,在mailbox为空时自动block住,而是会在0时刻一直不停地进行下去,形成一个死循环。

这样就可以了,判断条件满足时,如果什么都不做,就只加一个;

但加了时钟会存在一个问题,在满足que_q中有数据时gen不产生数据,有可能此时的que_q是drv将数据push到总线上之前判断的。push到总线上以后,que_q为空,但是该语句却并没有触发时间,从而导致下一个时钟周期虽然que_q里面没有数据,但是generator也没有产生数据,因此使得slex信号拉低,破坏了正常的二级流水。修正这个问题,比较粗糙的方法是将判断条件改成que_q.size()>1,这样即使该判断在push总线之前发生也会让gen产生数据。

到目前为止,driver中有3个并发进程,一个从mailbox里get数据,一个当get到数据以后进行master的功能模拟,一个在发送完以后告诉进程gen数据发送完毕,需要再传一个随机激励进来。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值