✎ 编 者 按
BusDriver会玩儿了,注入时序激励也就so easy了。
》Driver初始化都做了啥
在cocotb_bus里,其基本组件包含Bus,Driver,Monitor,Scoreboard三部分。今天了解下Driver部分。
先看下Driver基类。Driver一般不直接调用,其初始化函数为:
这里定义了两组变量:_pending与_sendQ,busy_event与busy。前者用于总线时序的驱动管理,_sendQ可用于接收用户待传输的数据。后者则定义实现了一套类似软件中“锁”的API接口:
在初始化函数中,起了一个_sendThread作为协程,可以先看下_sendThread里面都做了什么:
该协程起来后会一直运行,当其检测到_sendQ中存在任务时,会依次将_sendQ中的任务通过_send函数发送出去。当任务处理完后期将会等待有新的任务处理。
那再看_send函数的实现:
send函数很好理解,对于待处理的每个任务,通过调用_driver_send来发送任务,若用户有指定event,则触发对应的event。若有指定callback,则执行相应的callback。这里面_driver_send则是需要继承该类的类实现该函数:
也就意味着,核心的_driver_send函数是实现的核心。
》发送数据的两种形式
Driver中为发送数据提供了两种形式:send,append。
我们先看send函数:
send()函数可以理解为一个同步式操作,其会等待数据发送完成后函数才会退出。
而append函数实现机制则为:
append更类似于一个异步操作。其会将任务放到_sendQ队列中,随后触发_pending。从而通过_send_thread来进行任务的发送处理。通过append函数,可以指定每个任务对应的callback,event等动作。这个函数个人不建议使用,后面会给出原因和相应的example。
》其他方法
Driver还提供了两个其他的方法:
- clear():清除_sendQ。
- kill():删除退出_send_thread。
如果你在设计中倾向于使用同步式方法,那么完全可以在一开始就调用kill把闲置的_send_thread协程退出。
》BusDriver是关键
如上所述,Driver往往并不直接使用,在cocotb_bus中,BusDriver进一步扩展了Driver。BusDriver的初始化函数为:
可以看到,在其初始化函数中,针对待驱动的接口例化了一个Bus实现。不过这里值得注意的是_signals信号并没有定义就直接使用,也就意味着在继承BusDriver时我们必须要定义实现_signals,同时也可以重写_optional_signals信号。
前面提到过,Driver中的_driver_send需要在其子类中实现,在BusDriver中,_driver_send的实现机制为:
这里源代码中252行原版存在bug,这里建议按照上面的形式进行修改。
在该实现里,当sync有设置时,将会等待时钟上升沿到来时进行信号时许驱动,否则将直接进行时许驱动。在前面Driver中_send_thread实现里,倘若_sendQ中有大量任务需要处理,那么其仅在首个报文会等待时钟沿到来,而后边的任务都设置成了sync=False,将会导致所有的任务不等待时钟沿即进行驱动,导致所有的信号赋值都发生在一个时钟内,从而信号覆盖,并未达到想要的效果。
》Example
这里给出一个相关的example。测试DUT为:
测试程序为:
这里我们定义类pkg作为transaction。pkgInDrv中继承BusDriver。在pkgInDrv中我们定义了portInit函数用于初始化端口,用sendPkg来实现数据发送。在sendPkg中调用send函数时sync均设置成了False。由于send是同步的,所以在仿真中send的调用默认均为时钟上升沿到来后才进行调用,故无需再等待下一个时钟沿,同时该方式实现也能够产生两个报文紧挨着的效果。
生成的波形如下图所示:
》append示例
下面这个为append的使用example:
其波形效果如下图所示:
并未达到我们想要的效果。
☆ END ☆
作者:玉骐
原文链接:Spinal FPGA
微信公众号:
推荐阅读
更多SpinalHDL技术干货请关注Spinal FPGA欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。