4

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

这一般是如何实现的?

4

3 回答 3

2

您不会从 VPI(例如,从始终块)提前模拟时间 - 这会弄乱调度程序。您的 VPI 代码在零模拟时间内被调用;您创建新事务,并告诉调度程序何时对这些事务采取行动。您通常使用参数 3 和 4 来创建新事务vpi_put_value(例如,指定阻塞或非阻塞,或者在将来的某个时间)。这基本上正是您在 process/always 块中所做的事情 - 一旦您返回控制权,调度程序就会计算出要做什么。

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

编辑

vpi_put_delay回复您的评论:我认为如果您尝试在返回之前以不同的延迟多次调用它会做什么,我认为没有任何规范;您只看到最后一次延迟,这并不让我感到惊讶。或许有点像在一个总挡中对同一个对象有多个 NBA;最后一个获胜。如果您真的想在返回之前在同一个对象上安排多个事务,请使用vpi_put_delays, 并列出多个延迟(Sutherland 中的示例,p194)。

但有什么意义呢?这实质上为您提供了 VHDLafter功能,以及交易列表。它不会提前模拟时间。您同时将所有事务添加到事件队列。您无法在“稍后”模拟时间读取任何其他对象的值,因为没有更晚的模拟时间。请参阅 2005 LRM 中的 5.6.4(不是 SystemVerilog LRM;它是一种不同的语言)。这就像在一个总是块中具有多个 RHS 延迟的非阻塞分配 - 所有 RHS 操作数都被立即读取,没有提前时间,但 LHS 更新被安排在以后。

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

于 2013-06-12T09:19:31.763 回答
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;

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

如果您熟悉 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开发人员之一。

于 2014-01-03T23:41:49.863 回答
0

我找不到任何直接的例子。我能够在 LRM 中找到几个提到与 VPI 相关的“模拟时间”的地方。符合 LRM 是可能的;IEEE Std 1800-2012第 36.9.2 节第 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上的教程和第 3 部分的直接示例。

于 2013-06-12T16:41:35.843 回答