#cocotb—基础语法对照篇
cocotb
的出现使得我们能够在做RTL仿真验证时依托Python来进行测试用例的构建,当我们习惯了用Verilog
、SystemVerilog
来构建测试用例时,切换到cocotb后最直观的方式便是我们能够建立cocotb
中的基础语法与SystemVerilog
中仿真常用的关键字又一个对应,能够使我们又一个初步的对照。本篇就cocotb
中的基础语法与SystemVerilog
中的常用语法做一个对照总结。
##非阻塞赋值
在使用Systemverilog 进行仿真时,对于接口信号,往往建议采用非阻塞赋值进行操作,其符号为“<=”。
在cocotb中,对于信号的赋值,其提供相应的非阻塞赋值方式,其符号也同样为“<=”。
在cocotb的手册里提到:
The syntax sig <= new_value
is a short form of sig.value = new_value
. It not only resembles HDL syntax, but also has the same semantics: writes are not applied immediately
, but delayed until the next write cycle.
因而我们可以在cocotb中这样来进行非阻塞赋值:
# Get a reference to the "clk" signal and assign a value
clk = dut.clk
clk.value = 1
# Direct assignment through the hierarchy
dut.input_signal <= 12
# Assign a value to a memory deep in the hierarchy
dut.sub_block.memory.array[4] <= 2
##阻塞赋值
针对阻塞赋值(立即生效),cocotb提供了相应的语法:
setimmediatevalue(value)
因而对于阻塞赋值,我们在cocotb中可以这样写:
dut.input_signal.setimmediatevalue(1)
##信号值读取
对于信号的读取,我们在SystemVerilog中,可以直接读取信号值,而在cocotb中,其为接口变量提供了value方法属性用于获取信号值。
读取方式:
sig.value
返回类型:
BinaryValue
Accessing the
value
property of a handle object will return aBinaryValue
object. Any unresolved bits are preserved and can be accessed using thebinstr attribute
, or a resolved integer value can be accessed using theinteger attribute
. 信号的读取我们可以这么来写:
# Read a value back from the DUT
count = dut.counter.value
print(count.binstr)
1X1010
# Resolve the value to an integer (X or Z treated as 0)
print(count.integer)
42
# Show number of bits in a value
print(count.n_bits)
6
##Time
在仿真里延迟等待是经常遇到的,在cocotb
里,我们通过Timer
来实现延迟:
cocotb.triggers.Timer(time_ps, units=None)
Parameters
time_ps
(numbers.Real or decimal.Decimal) – The time value. Note that despite the name this is not actually in picoseconds but depends on the units argument.
units
(str or None, optional) – One of None, 'fs', 'ps', 'ns', 'us', 'ms', 'sec'. When no units is given (None) the timestep is determined by the simulator.
由于cocotb
是基于协程的,而延迟函数的执行的时间长度是依赖于仿真器的,因此Timer
延迟的执行需调用await
:
await Timer(1, units='ns')
##边沿检测
在SystemVerilog
中我们常用posedge
、negedge
来检测上升沿和下降沿,在cocotb
里,针对边沿检测,其提供了四个调用:
等待跳变
class cocotb.triggers.Edge(*args, **kwargs)
Fires on any value change of signal.
等待上升沿
class cocotb.triggers.RisingEdge(*args, **kwargs)
Fires on the rising edge of signal, on a transition from 0 to 1.
等待下降沿
class cocotb.triggers.FallingEdge(*args, **kwargs)
Fires on the falling edge of signal, on a transition from 1 to 0.
检测等待指定到个数边沿
class cocotb.triggers.ClockCycles(signal,num_cycles,rising=True)
Fires after num_cycles transitions of signal from 0 to 1.
Parameters
signal
– The signal to monitor.
num_cycles (int)
– The number of cycles to count.
rising (bool, optional)
– If True, the default, count rising edges. Otherwise, count falling edges.
我们在使用时,可以这么来写:
#等待信号signalA发生变化
await cocotb.triggers.Edge(dut.signalA)
#等待signalA从0变为1
await cocotb.triggers.RisingEdge(dut.signalA)
#等待signalA从1变为0
await cocotb.triggers.FallingEdge(dut.signalA)
#等待signalA从0变为1三次
await cocotb.triggers.ClockCycles(dut.signalA,3,true)
##fork-join_none
SystemVerilog
中的fork-join_none
用于发起一个线程但不等待线程的结束,在cocotb
中,相应的语法为fork
:
cocotb.fork()
Schedule a coroutine to be run concurrently
在写仿真代码时,我们可以这么写:
async def reset_dut(reset_n, duration_ns):
reset_n <= 0
await Timer(duration_ns, units='ns')
reset_n <= 1
reset_n._log.debug("Reset complete")
reset_thread = cocotb.fork(reset_dut(reset_n, duration_ns=500))
这里值得注意的是,由于fork是起一个协程,因而resut_dut需添加async声明。
##fork-join
与SystemVerilog
中相对应的,cocotb
等待一个协程的结束同样提供了join
方法:
class cocotb.triggers.Join(*args, **kwargs)
Fires when a fork()ed coroutine completes.
The result of blocking on the trigger can be used to get the coroutine result:
使用方式:
async def coro_inner():
await Timer(1, units='ns')
return "Hello world"
task = cocotb.fork(coro_inner())
result = await Join(task)
assert result == "Hello world"
##fork-any
相较于SystemVerilog
中的join-any
语法,cocotb
并无专门的对应语法,但却有相似的方法供调用:
class cocotb.triggers.First(*triggers)
等待第一个协程结束即返回
t1 = Timer(10, units='ps')
t2 = Timer(11, units='ps')
t_ret = await First(t1, t2)
这里我们通过First
等待t1、t2
第一个返回的结果后await
结束,并将第一个返回的协程的返回结果赋值给t_ret
。
##event
对于SystemVerilog
中的event
,在cocotb
中同样提供类似的event
:
class cocotb.triggers.Event(name=None)
用于两个协程间的同步
方法:
set(data=None)
:唤醒所有等待该事件的协程
wait()
: 等待事件的触发(await)
,如果事件已经触发,立即返回
clear()
: 清除已触发的事件
is_set()
: 判断事件是否触发
##旗语
cocotb
中提供了Lock
操作用来实现与SystemVerilog
中相似的操作,不过Lock
不可声明旗语为多个:
class cocotb.triggers.Lock(name=None)
方法:
locked : True if the lock is held.
acquire() :Produce a trigger which fires when the lock is acquired.
release(): Release the lock.
##mailbox
SystemVerilog
中的mailbox
主要用于不同进程间的通信,在cocotb
中,普通的Python
的队列即可实现该功能(协程中无需没有进程间同步问题)。
作者:七夜浮生
版权:本作品采用署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
欢迎各位大佬评论交流。可以的话,不妨点一下推荐。
本文章使用limfx的vscode插件快速发布