使用Verilog VPI在模拟器中提前时间

时间:2013-06-11 12:33:31

标签: testing verilog verification

我想使用VPI编写一个Verilog任务来对接口进行bit-bang;但是,我无法弄清楚如何从VPI任务推进仿真时间。我目前正在使用Mentor Graphics Questa,但也可以访问Icarus Verilog。我已成功强制我感兴趣控制的端口上的值,但即使在vpi_put_value上指定了延迟,模拟时间也不会提前。

这是如何实现的?

3 个答案:

答案 0 :(得分:1)

您不会从VPI(或来自总是块)推进模拟时间 - 这会使调度程序混乱。您的VPI代码在零sim时间内被调用;您创建新事务,并告诉调度程序何时对这些事务执行操作。您通常使用参数3和4到vpi_put_value创建新事务(例如,指定阻塞或非阻塞,或将来的某个时间)。这基本上就是你在进程/总是阻塞中所做的 - 一旦你返回控制,调度程序就会知道该做什么。

因此,简而言之,使用您的C代码来安排未来(或即时)事务,然后返回,以便调度程序可以确定要执行的操作。你不能在C代码中间放置(模拟)等待。我想,在你的情况下,你想要做的是在Verilog中有一个主控制循环,带有#10的东西,并在适当的时候调用你的C代码。

修改

重新发表您的评论:如果您在返回前尝试多次调用不同的延迟,我认为vpi_put_delay将会执行的操作没有任何规范。你只看到最后的延迟并不让我感到惊讶。也许有点像在一个块中对同一个对象有多个NBAs;最后一个获胜。如果您确实想在返回之前在同一对象上安排多个事务,请使用vpi_put_delays,并列出多个延迟(例如Sutherland,第194页)。

但重点是什么?这实际上为您提供了VHDL after功能,以及一系列事务。它不会提前模拟时间。您将所有事务同时添加到事件队列 。您无法在“稍后”模拟时读取任何其他对象的值,因为没有稍后的模拟时间。请参阅2005 LRM中的5.6.4(不是SystemVerilog LRM;它是另一种语言)。这就像在一个总块中具有RHS延迟的多个非阻塞分配 - 所有RHS操作数都立即读取,而不会延长时间,但LHS更新计划在以后进行。

忘记时间推进。你不能这样做,因为你会搞乱调度程序队列中的其他所有内容。如果其他人在您提前的时间安排了更新,该怎么办?您需要将控制权返回给Verilog代码(即调度程序),以便调度程序可以提前计时。

答案 1 :(得分:1)

我知道这个答案有点迟了但我不确定以前的任何答案是否都解决了您的原始请求,尽管它们并不正确。

您无法在一个模拟步骤中多次在同一个句柄上调用vpi_put_value以及时排队值。只有最后的通话才会有效。

您要做的是调用vpi_put_value强制您想要的值,然后使用cbAfterDelay注册回调,以便下次更改值。当您的回调处理程序执行时,模拟时间将提前到该点,因此您可以将下一个值强制到句柄上。

您的回调函数会执行以下操作:

static int32_t handle_vpi_callback(p_cb_data cb_data) {
    // advance your driver state using user_data to retrieve current state
    // call vpi_put_value with new signal value
    // determine next time value requiring action
    // register another callback
}

并注册定时回调:

s_cb_data cb_data_s;
s_vpi_time vpi_time_s;
p_vpi_cb_user_data user_data;

vpi_time_s.type = vpiSimTime;
vpi_time_s.high = (uint32_t)(time_ps>>32);
vpi_time_s.low  = (uint32_t)(time_ps);

cb_data_s.reason    = cbAfterDelay;
cb_data_s.cb_rtn    = handle_vpi_callback;
cb_data_s.obj       = NULL;
cb_data_s.time      = &vpi_time_s;
cb_data_s.value     = NULL;
cb_data_s.user_data = (char *)user_data;

vpiHandle new_hdl = vpi_register_cb(&cb_data_s);

这与Verilog代码的行为相同:

#10 value = something;
#5  value = something_else;

如果您的总线与时钟同步,您可能只想使用cbAfterDelay驱动时钟(或从Verilog中的进程驱动它)并提前驱动程序状态注册cbValueChange时钟本身。这将模拟在模拟器中运行的时钟进程。

如果您熟悉Python,您可能会对名为Cocotb的开源项目感兴趣,该项目将VPI抽象为您在模拟器中运行的被测设备(DUT)提供一个漂亮的Pythonic接口。如果你有想要接口的现有C ++代码,那么通过Python可能会节省你一些时间。

例如,要通过接口发送缓冲区,驱动程序发送例程可能如下所示:

def send(dut, buffer):
    """Send a buffer over a packetised bus"""

    bus_width = len(dut.data) / 8
    firstword = True

    while buffer:
        yield RisingEdge(dut.clk)

        nbytes = min(len(buffer), bus_width)
        dut.data.value = buffer[:nbytes]
        dut.valid.value = 1
        dut.startofpacket.value = int(firstword)

        if nbytes <= buswidth:
            dut.endofpacket.value = 1
            dut.empty.value = bus_width - len(buffer)
            buffer = ""
        else:
            buffer = buffer[buswidth:]

        firstword = False

    yield RisingEdge(dut.clk)
    dut.valid.value = 0
    dut.endofpacket.value = 0

免责声明:我是Cocotb开发人员之一。

答案 2 :(得分:0)

我无法找到任何直接的例子。我能够在LRM中找到几个与VPI相关的“模拟时间”的地方。根据LRM,有可能; IEEE Std 1800-2012第2段第36.9.2节规定:

  

“VPI模拟回调设施应提供应用程序   通过与工具动态交互的方法,检测   价值发生变化, 时间推进 ,模拟结束,   等“。

以下是其他地方时间提到VPI:

  • 37.79时间队列
  • 38.10 vpi_get_delay()
  • 38.13 vpi_get_time()
  • 38.32 vpi_put_delay()
  • 38.34 vpi_put_value()有一个time_p组件
  • 38.36 vip_register_cb()
    • 38.36.1.1 提及vpiSimTime和vpiSuppressTime
    • 38.36.2模拟时间回调

如果所有其他方法都失败了,请尝试使用SystemC(IEEE Std 1666-2011)。 http://www.asic-world.com/systemc/systemc_time.html上的教程以及part 3上的直接示例。