在仿真里,信号的驱动究竟是在时钟沿之前还是在时钟沿之后? 》关于仿真中信号驱动那点事儿 记得在SystemVerilog中,对于仿真时信号的驱动绿皮书里有这么两个建议: 时钟信号驱动赋值采用=。 其他信号的驱动赋值采用<=形式。 乍一看有点儿像设计里阻塞赋值与非阻塞赋值的差异。当然仿真里是没有这个概念的。具体底层仿真器的原理也不在我个人的研究范围内(不求甚解)。无论是SpinalHDL还是cocotb,其均是基于协程的思路来进行的仿真。那么,这里面在仿真信号赋值时,今天来一探究竟。 》SpinalHDL中的仿真信号驱动 我们以下面这个简单的example来进行测试:
这里dataOut我们使其复位时处于高电平。 所采用的仿真代码为:
我们来看下其仿真波形:
这里从波形上看,当复位信号被释放的同时,dataOut随机被翻转。不妨一同来看下背后的实现机制。 首先,我们来看下forkStimulus里面的实现逻辑:
这里,首先对时钟信号和复位信号进行初始化,结合我们时钟的配置,这里时钟信号首先被设置为低电平,复位信号被释放掉,随后拉起一个doStimulus的协程,再看下doStimulus的实现:
这里我们采用的是同步复位,我们来看其处理流程。首先复位信号被拉起,此时clk为低电平,随后clk被重复拉起释放了16个时钟周期退出for循环。此时clk为低电平。紧接着复位信号被释放,进入DoClock函数。DoClock函数实现为:
这里是简单的时钟信号驱动函数,按照指定周期产生时钟信号。 而再看waitSampling()函数的实现:
若时钟是上升沿有效,那么该函数推出的条件是已经采样到时钟信号从低电平到高电平变化完成之后才推出。那么我们在随后对dataIn的赋值其实是发生在clk上升沿到来后的0+时间才发生的。 从这里代码分析我们可以看出,在SpinalHDL的仿真里: 复位信号会持续16个时钟周期,并在下一个时钟周期上升沿到来之前信号将复位信号释放(在下一个时钟沿0-时刻到来)。也正因如此,从仿真波形上看,dataOut和reset似乎同时发生了改变。 waitSampling()之后对信号的操作是发生时钟有效沿跳变之后才进行的,即0+时刻。 在SpinalHDL中没有=与<=两种赋值,无论是时钟还是普通信号,都采用类似阻塞赋值的意思。 》cocotb中的仿真信号驱动 cocotb仿真的实现机制和SpinalHDL原理无差。在cocotb中,对于信号的赋值,也和SystemVerilog提供了两种类似的方式: sig.value=new_value sig.setimmediatevalue(new_val) 前者有点儿类似非阻塞赋值,而后者类似阻塞赋值。 这里我们以类似的dut来进行测试:
我们对复位信号的驱动采用两种不同的形式来进行对比测试: 首先采用“非阻塞”赋值的方式:
其仿真波形如下图所示:
可以看到,从波形上来看,dataOut的变化比reset拉低完了一个时钟周期,也就意味着reset信号的拉低是发生在时钟跳变沿产生之后的0+时刻,在该时钟沿采样时dataOut仍被处于复位状态。 再来看下采用“阻塞”赋值的方式:
其仿真波形如下:
而在这里,从波形上来看,dataOut在reset跳变的同一时钟沿发生了变化,那么也就意味着在时钟沿到来的0-时刻,reset发生了变化,故而dataOut在该时钟沿也发生了跳变。
原作者:玉骐
|