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 valueclk = dut.clkclk.value = 1
# Direct assignment through the hierarchydut.input_signal <= 12
# Assign a value to a memory deep in the hierarchydut.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 a BinaryValue object. Any unresolved bits are preserved and can be accessed using the binstr attribute, or a resolved integer value can be accessed using the integer attribute.

    信号的读取我们可以这么来写:

 

# Read a value back from the DUTcount = dut.counter.valueprint(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 valueprint(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变为1await cocotb.triggers.RisingEdge(dut.signalA)#等待signalA从1变为0await 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的队列即可实现该功能(协程中无需没有进程间同步问题)。

审核编辑 :李倩

 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分