Verilog ACC 子程序列表

Verilog ACC(Access)子程序列表

在 Verilog 仿真中,ACC(Access)子程序是 Verilog PLI(Programming Language Interface)的一部分,用于与仿真器交互,访问和操作 Verilog 模型的内部数据结构(如信号、模块、实例等)。ACC 子程序由 IEEE 1364 标准定义,主要用于仿真环境中的调试、测试和数据提取,不适用于逻辑综合,因为它们是仿真专用的接口,无法映射到硬件。

以下是 Verilog ACC 子程序的概述、分类、常见子程序列表,以及使用注意事项。鉴于 ACC 子程序通常用于与 C/C++ 接口开发复杂仿真功能,我会尽量简洁清晰,并提供一个简单的示例说明其用法。


ACC 子程序概述

  • 作用
  • ACC 子程序允许用户通过 C/C++ 程序访问 Verilog 仿真器的内部数据,如信号值、模块层次、时间信息等。
  • 常用于自定义仿真工具、测试平台、波形生成或与外部软件交互。
  • 使用场景
  • 读取/修改信号值(如 $monitor 的增强版)。
  • 遍历设计层次结构(如模块、实例、端口)。
  • 控制仿真流程(如暂停、获取时间)。
  • 不可综合性
  • ACC 子程序仅用于仿真环境(如 ModelSim、VCS),无法综合为硬件。
  • 逻辑综合时,应避免在 Verilog 代码中使用 PLI 调用。
  • 调用方式
  • 在 Verilog 代码中通过系统任务(如 $acc_task)调用 C 函数。
  • 在 C/C++ 代码中,使用 ACC 库函数(如 acc_fetch_value)操作仿真数据。

ACC 子程序分类

ACC 子程序按功能分为以下几类(基于 IEEE 1364-2005 标准):

  1. 对象访问
  • 用于获取 Verilog 对象(如模块、信号、端口)的句柄(handle)。
  • 示例:acc_handle_tfargacc_handle_object
  1. 值操作
  • 读取或修改信号值、参数值。
  • 示例:acc_fetch_valueacc_set_value
  1. 层次遍历
  • 遍历设计层次,如模块、实例、端口、网线。
  • 示例:acc_next_childacc_next_port
  1. 时间与控制
  • 获取仿真时间、控制仿真流程。
  • 示例:acc_fetch_timescaleacc_set_interactive_scope
  1. 属性查询
  • 获取对象属性(如信号类型、宽度、方向)。
  • 示例:acc_fetch_typeacc_fetch_direction
  1. 错误与调试
  • 处理错误、输出调试信息。
  • 示例:acc_error_flagacc_vcl_add(值变化检测)。

常见 ACC 子程序列表

以下是常用的 ACC 子程序,按功能分类,包含简要说明和典型用法:

  1. 对象访问
  • acc_handle_tfarg(int n):获取系统任务第 n 个参数的句柄。
    • 用法:handle = acc_handle_tfarg(1);(获取 $acc_task(arg1) 的句柄)
  • acc_handle_object(char *name):通过名称获取对象句柄。
    • 用法:handle = acc_handle_object("top.module1.sig1");
  • acc_handle_tfinst():获取当前系统任务实例的句柄。
    • 用法:handle = acc_handle_tfinst();
  1. 值操作
  • acc_fetch_value(handle, char *format):读取对象的值(格式如 "b" 二进制、"d" 十进制)。
    • 用法:char *val = acc_fetch_value(sig_handle, "b");
  • acc_set_value(handle, value, mode):设置对象的值。
    • 用法:acc_set_value(sig_handle, &new_val, accForce);
  • acc_fetch_paramval(handle):获取参数值。
    • 用法:double val = acc_fetch_paramval(param_handle);
  1. 层次遍历
  • acc_next_child(handle, prev_handle):获取模块的下一个子模块。
    • 用法:child_handle = acc_next_child(module_handle, NULL);
  • acc_next_port(handle, prev_handle):获取模块的下一个端口。
    • 用法:port_handle = acc_next_port(module_handle, NULL);
  • acc_next_net(handle, prev_handle):获取模块的下一个网线。
    • 用法:net_handle = acc_next_net(module_handle, NULL);
  1. 时间与控制
  • acc_fetch_timescale(handle):获取模块的时间刻度。
    • 用法:char *ts = acc_fetch_timescale(module_handle);
  • acc_fetch_simtime():获取当前仿真时间。
    • 用法:double time = acc_fetch_simtime();
  • acc_set_interactive_scope(handle):设置交互作用域。
    • 用法:acc_set_interactive_scope(module_handle);
  1. 属性查询
  • acc_fetch_type(handle):获取对象类型(如模块、寄存器、网线)。
    • 用法:int type = acc_fetch_type(handle);
  • acc_fetch_fullname(handle):获取对象全路径名。
    • 用法:char *name = acc_fetch_fullname(handle);
  • acc_fetch_direction(handle):获取端口方向(输入、输出、双向)。
    • 用法:int dir = acc_fetch_direction(port_handle);
  1. 值变化检测(VCL)
  • acc_vcl_add(handle, callback, user_data, vcl_reason):注册值变化回调。
    • 用法:acc_vcl_add(sig_handle, monitor_cb, NULL, vcl_value_change);
  • acc_vcl_delete(handle, callback):删除值变化回调。
    • 用法:acc_vcl_delete(sig_handle, monitor_cb);

使用 ACC 子程序的注意事项

  1. 仿真专用
  • ACC 子程序仅用于仿真环境(如 VCS、ModelSim),不可用于综合(如 Vivado、Design Compiler)。
  • 综合代码应使用纯 Verilog 构造(如 alwaysassign)。
  1. 环境配置
  • 需要链接 ACC 库(通常由仿真工具提供)。
  • C/C++ 代码需包含 acc_user.h
    c #include "acc_user.h"
  1. 性能开销
  • ACC 子程序涉及 C/Verilog 交互,可能影响仿真速度。
  • 尽量减少频繁调用(如值变化检测仅用于关键信号)。
  1. 兼容性
  • ACC 是 Verilog-2001/2005 标准的一部分,现代仿真器(如 VCS、QuestaSim)支持良好。
  • 部分子程序可能因工具而异,需查阅工具文档。
  1. 调试
  • 使用 acc_error_flag 检查错误。
  • 使用 acc_fetch_fullname 确认对象路径。

示例:使用 ACC 子程序监控信号值

以下是一个简单的 Verilog 和 C 代码示例,展示如何使用 ACC 子程序监控 Verilog 模块中信号的变化。

Verilog 代码(test.v)

module test (
    input wire clk,
    input wire rst_n,
    output reg [3:0] count
);
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) count <= 4'b0;
        else count <= count + 1;
    end

    // 调用自定义 PLI 任务
    initial begin
        $monitor_count(count);
    end
endmodule

C 代码(monitor_count.c)

#include "acc_user.h"
#include <stdio.h>

// 回调函数:当信号值变化时调用
void monitor_callback(handle sig, char *user_data) {
    char *value = acc_fetch_value(sig, "b"); // 获取二进制值
    char *name = acc_fetch_fullname(sig);   // 获取信号全路径
    printf("Time: %g, Signal: %s, Value: %s\n", 
           acc_fetch_simtime(), name, value);
}

// PLI 任务:注册信号监控
int monitor_count_calltf(char *user_data) {
    handle arg_handle = acc_handle_tfarg(1); // 获取第一个参数(count)
    if (arg_handle == NULL) {
        printf("Error: Cannot get handle for argument\n");
        return 0;
    }
    // 注册值变化回调
    acc_vcl_add(arg_handle, monitor_callback, NULL, vcl_value_change);
    return 0;
}

// PLI 注册函数
void acc_register() {
    s_tfcell veriusertfs[] = {
        {usertask, 0, 0, 0, monitor_count_calltf, 0, "$monitor_count"},
        {0} // 结束标志
    };
    acc_initialize();
    acc_configure(accEnableArgs, "true");
    acc_configure(accDisplayWarnings, "true");
    acc_register_tfs(veriusertfs);
}

编译与运行(以 VCS 为例):

# 编译 Verilog 和 C 代码
vcs -full64 -P pli.tab test.v monitor_count.c -o simv
# 运行仿真
./simv

pli.tab 文件

$monitor_count call=monitor_count_calltf acc=rw:%1

输出示例

Time: 5, Signal: test.count, Value: 0001
Time: 15, Signal: test.count, Value: 0010
Time: 25, Signal: test.count, Value: 0011
...

说明

  • Verilog 模块 test 是一个 4 位计数器。
  • C 代码通过 $monitor_count(count) 注册信号 count 的值变化监控。
  • acc_handle_tfarg 获取信号句柄,acc_vcl_add 注册回调,acc_fetch_value 读取值。
  • 每次 count 变化,C 函数打印时间、信号名和值。

扩展建议

  1. 复杂信号监控
  • 使用 acc_next_net 遍历模块所有信号,批量注册监控:
    c handle module = acc_handle_object("test"); handle net = NULL; while ((net = acc_next_net(module, net)) != NULL) { acc_vcl_add(net, monitor_callback, NULL, vcl_value_change); }
  1. 与前端交互
  • 将 ACC 获取的数据通过 socket 或文件传输到前端(如 JavaScript 网页)。
  • 示例:将信号值写入文件,JavaScript 读取并显示:
    c FILE *fp = fopen("output.txt", "a"); fprintf(fp, "%g %s %s\n", acc_fetch_simtime(), name, value); fclose(fp);
  1. 动态控制仿真
  • 使用 acc_set_value 动态修改信号值:
    c s_acc_value new_val = {accBinVal, "1010"}; acc_set_value(sig_handle, &new_val, accForce);
  1. 替代方案
  • 现代设计中,推荐使用 SystemVerilog DPI(Direct Programming Interface)替代 ACC,语法更简洁。
  • 示例(SystemVerilog DPI):
    verilog import "DPI-C" function void monitor_count(input int count);

注意事项与限制

  1. 不可综合
  • ACC 子程序仅用于仿真,不能用于逻辑综合。
  • 综合代码需使用纯 Verilog 构造(如之前的计数器示例)。
  1. 工具依赖
  • ACC 子程序实现因仿真器(VCS、ModelSim、QuestaSim)而异,需参考工具手册。
  • 确保链接正确的 ACC 库(如 -lacc)。
  1. 性能
  • 频繁调用 ACC(如监控大量信号)可能降低仿真速度。
  • 使用 acc_vcl_add 仅监控关键信号。
  1. 调试
  • 检查 acc_error_flag 捕获错误。
  • 使用 acc_fetch_fullname 验证对象路径。

参考与资源

  • 标准:IEEE 1364-2005 Verilog PLI 标准,定义 ACC 子程序。
  • 书籍
  • 《Verilog PLI Handbook》(Stuart Sutherland):详细讲解 ACC 和 PLI。
  • 《Writing Testbenches》(Janick Bergeron):测试平台开发。
  • 工具文档
  • Synopsys VCS User Guide:PLI 和 ACC 配置。
  • Mentor QuestaSim User Manual:ACC 子程序支持。
  • 开源资源
  • Verilog PLI 示例:GitHub 上有许多 PLI 教程和代码。

总结

ACC 子程序是 Verilog PLI 的核心,用于仿真环境中访问和操作设计数据,常见功能包括信号监控、值修改和层次遍历。上述示例展示了如何使用 acc_vcl_add 监控信号变化,适合调试和测试平台开发。但 ACC 子程序不可综合,实际硬件设计应使用可综合 Verilog 代码(如状态机、计数器)。

如果需要更复杂的 ACC 示例(如遍历模块层次、与前端交互或集成到特定仿真器),或希望转向 SystemVerilog DPI,请提供具体需求!我也可以结合之前的可综合计数器示例,扩展为带 PLI 监控的完整测试平台。

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注