案例四:双端口栈(协程)
双端口栈简介与环境构建
本案例中使用的双端口栈与案例三中的实现完全相同,请查看案例三中的双端口栈简介及构建驱动环境。
利用协程驱动 DUT
在案例三中,我们使用了回调函数的方式来驱动DUT,回调函数虽然给我们提供了一种能够完成并行操作的方式,然而其却把完成的执行流程割裂为多次函数调用,并需要维护大量中间状态,导致代码的编写及调试变得较为复杂。
在本案例中,我们将会介绍一种通过协程驱动的方法,这种方法不仅能够做到并行操作,同时能够很好地避免回调函数所带来的问题。
协程简介
协程是一种“轻量级”的线程,通过协程,你可以实现与线程相似的并发执行的行为,但其开销却远小于线程。其实现原理是,协程库实现了一个运行于单线程之上的事件循环(EventLoop),程序员可以定义若干协程并且加入到事件循环,由事件循环负责这些协程的调度。
一般来说,我们定义的协程在执行过程中会持续执行,直到遇到一个需要等待的“事件”,此时事件循环就会暂停执行该协程,并调度其他协程运行。当事件发生后,事件循环会再次唤醒该协程,继续执行。
对于硬件验证中的并行执行来说,这种特性正是我们所需要的,我们可以创建多个协程,来完成验证中的多个驱动任务。我们可以将时钟的执行当做事件,在每个协程中等待这个事件,当时钟信号到来时,事件循环会唤醒所有等待的协程,使其继续执行,直到他们等待下一个时钟信号。
我们用 Python 中的 asyncio
来实现对协程的支持:
你可以直接运行上述代码来观察协程的执行过程。在上述代码中我们用 create_task
创建了两个协程任务并加入到事件循环中,每个协程任务中,会不断打印一个数字并等待下一个时钟信号到来。
我们使用 dut.RunStep(10)
来创建一个后台时钟,它会不断产生时钟同步信号,使得其他协程能够在时钟信号到来时继续执行。
基于协程驱动的双端口栈
利用协程,我们就可以将驱动双端口栈中单个端口逻辑写成一个独立的执行流,不需要再去维护大量的中间状态。
下面是我们提供的一个简单的使用协程驱动的验证代码:
与案例三类似,我们定义了一个 SinglePortDriver
类,用于驱动单个端口的逻辑。在 main
函数中,我们创建了两个 SinglePortDriver
实例,分别用于驱动两个端口。我们将两个端口的驱动过程放在了入口函数 main
中,并通过 asyncio.create_task
将其加入到事件循环中,在最后我们通过 dut.RunStep(200)
来创建了后台时钟,以推动测试的进行。
该代码实现了与案例三一致的测试逻辑,即在每个端口中对栈进行 10 次 PUSH 和 10 次 POP 操作,并在操作完成后添加随机延迟。但你可以清晰的看到,利用协程进行编写,不需要维护任何的中间状态。
SinglePortDriver 逻辑
在 SinglePortDriver
类中,我们将一次操作封装为 exec_once
这一个函数,在 main
函数中只需要首先调用 10 次 exec_once(is_push=True)
来完成 PUSH 操作,再调用 10 次 exec_once(is_push=False)
来完成 POP 操作即可。
在 exec_once
函数中,我们首先调用 send_req
函数来发送请求,然后调用 receive_resp
函数来接收响应,最后通过等待随机次数的时钟信号来模拟延迟。
send_req
和 receive_resp
函数的实现逻辑类似,只需要将对应的输入输出信号设置为对应的值,然后等待对应的信号变为有效即可,可以完全根据端口的执行顺序来编写。
类似地,我们使用 StackModel
类来模拟栈的行为,在 commit_push
和 commit_pop
函数中分别模拟了 PUSH 和 POP 操作,并在 POP 操作中进行了数据的比较。
运行测试
在picker_out_dual_port_stack文件夹中创建example.py
将上述代码复制到其中,然后执行以下命令:
可直接运行本案例的测试代码,你将会看到类似如下的输出:
在输出中,你可以看到每次 PUSH
和 POP
操作的数据,以及每次 POP
操作的结果。如果输出中没有错误信息,则表示测试通过。
协程驱动的优劣
通过协程函数,我们可以很好地实现并行操作,同时避免了回调函数所带来的问题。每个独立的执行流都能被完整保留,实现为一个协程,大大方便了代码的编写。
然而,在更为复杂的场景下你会发现,实现了众多协程,会使得协程之间的同步和时序管理变得复杂。尤其是你需要在两个不与DUT直接交互的协程之间进行同步时,这种现象会尤为明显。
在这时候,你就需要一套协程编写的规范以及验证代码的设计模式,来帮助你更好地编写基于协程的验证代码。因此,我们提供了 toffee 库,它提供了一套基于协程的验证代码设计模式,你可以通过使用 toffee 来更好地编写验证代码,你可以在这里去进一步了解 toffee。