Verilog TF 子程序列表

Verilog TF(Task and Function)子程序列表

Verilog TF(Task and Function)子程序是 Verilog PLI(Programming Language Interface)的一部分,允许用户通过 C/C++ 程序与 Verilog 仿真器交互,主要用于操作系统任务(system tasks)或系统函数(system functions)的参数和行为。TF 子程序由 IEEE 1364 标准定义,主要用于仿真环境(不可综合),常用于测试平台、调试工具或与外部程序交互。

以下是 Verilog TF 子程序的概述、分类、常见子程序列表、使用注意事项,以及一个简单的示例代码。TF 子程序与 ACC 子程序(之前讨论)类似,但专注于系统任务/函数的调用和参数处理,而 ACC 更侧重于访问 Verilog 模型的内部结构。


TF 子程序概述

  • 作用
  • TF 子程序用于在 C/C++ 中处理 Verilog 系统任务(如 $my_task(arg1, arg2))或系统函数(如 $my_func(arg1))的参数、返回值和调用行为。
  • 提供对任务/函数调用上下文的访问,如参数值、实例信息、仿真时间等。
  • 使用场景
  • 自定义系统任务(如 $monitor 的增强版)。
  • 读取/修改任务参数值。
  • 控制仿真流程(如暂停、错误处理)。
  • 与外部工具交互(如生成波形文件、与前端通信)。
  • 不可综合性
  • TF 子程序仅用于仿真环境(如 VCS、ModelSim),无法综合为硬件。
  • 综合代码应使用纯 Verilog 构造(如 alwaysassign)。
  • 调用方式
  • 在 Verilog 代码中定义系统任务/函数(如 $my_task)。
  • 在 C/C++ 中实现对应的 TF 子程序,处理参数和逻辑。
  • 通过 PLI 注册表(pli.tabveriusertfs)将 C 函数与 Verilog 任务关联。

TF 子程序分类

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

  1. 参数访问
  • 获取或设置系统任务/函数的参数值。
  • 示例:tf_getptf_putp
  1. 实例信息
  • 获取任务/函数的调用实例、模块路径等。
  • 示例:tf_mipnametf_nodeinfo
  1. 时间操作
  • 获取仿真时间、时间刻度。
  • 示例:tf_gettimetf_gettimeunit
  1. 控制与错误
  • 控制任务执行、报告错误。
  • 示例:tf_dostoptf_error
  1. 字符串与格式化
  • 处理字符串参数或格式化输出。
  • 示例:tf_strgetptf_sprintf

常见 TF 子程序列表

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

  1. 参数访问
  • tf_getp(int n):获取第 n 个参数的值(整数或逻辑值)。
    • 用法:int val = tf_getp(1);(获取 $my_task(arg1)arg1 值)
  • tf_putp(int n, int val):设置第 n 个参数的值。
    • 用法:tf_putp(1, 42);(设置参数值为 42)
  • tf_getrealp(int n):获取第 n 个参数的浮点值。
    • 用法:double val = tf_getrealp(1);
  • tf_putrealp(int n, double val):设置第 n 个参数的浮点值。
    • 用法:tf_putrealp(1, 3.14);
  • tf_strgetp(int n, int format):获取第 n 个参数的字符串值。
    • 用法:char *str = tf_strgetp(1, 'b');(获取二进制字符串)
  1. 实例信息
  • tf_mipname():获取当前任务/函数调用的模块实例路径。
    • 用法:char *path = tf_mipname();(返回如 "top.module1"
  • tf_spname():获取当前任务/函数的名称。
    • 用法:char *name = tf_spname();(返回如 "$my_task"
  • tf_nodeinfo(int n, s_tfnodeinfo *info):获取第 n 个参数的节点信息(如类型、宽度)。
    • 用法:
      c s_tfnodeinfo info; tf_nodeinfo(1, &info);
  1. 时间操作
  • tf_gettime():获取当前仿真时间(以时间单位为基准)。
    • 用法:int time = tf_gettime();
  • tf_gettimeunit():获取当前模块的时间单位。
    • 用法:int unit = tf_gettimeunit();(返回如 -9 表示 ns)
  • tf_gettimeprecision():获取仿真精度。
    • 用法:int precision = tf_gettimeprecision();
  1. 控制与错误
  • tf_dostop():暂停仿真,进入交互模式。
    • 用法:tf_dostop();
  • tf_error(char *fmt, ...):报告错误信息。
    • 用法:tf_error("Invalid parameter: %d", val);
  • tf_warning(char *fmt, ...):报告警告信息。
    • 用法:tf_warning("Value out of range");
  1. 字符串与格式化
  • tf_sprintf(char *buf, char *fmt, ...):格式化字符串到缓冲区。
    • 用法:tf_sprintf(buf, "Value: %d", tf_getp(1));
  • tf_getcstringp(int n):获取第 n 个参数的 C 字符串。
    • 用法:char *str = tf_getcstringp(1);
  1. 任务控制
  • tf_nump():获取任务/函数的参数数量。
    • 用法:int num = tf_nump();
  • tf_synchronize():强制仿真同步到当前时间。
    • 用法:tf_synchronize();

使用 TF 子程序的注意事项

  1. 仿真专用
  • TF 子程序仅用于仿真环境(如 VCS、ModelSim),不可综合为硬件。
  • 综合代码需使用可综合 Verilog(如 alwaysassign)。
  1. 环境配置
  • 需要包含 vpi_user.h 或工具特定的 PLI 头文件:
    c #include "pli.h" // 工具特定,可能为 vpi_user.h
  • 链接仿真器提供的 PLI 库(如 -lpli)。
  1. 性能开销
  • 频繁调用 TF 子程序(如参数读取)可能降低仿真速度。
  • 优化:缓存参数句柄,避免重复调用 tf_getp
  1. 兼容性
  • TF 子程序基于 Verilog-2001/2005 标准,现代仿真器支持良好。
  • 不同仿真器(如 VCS、QuestaSim)可能有细微差异,需查阅文档。
  1. 错误处理
  • 使用 tf_errortf_warning 报告问题。
  • 检查参数有效性:
    c if (tf_nump() < 1) tf_error("Missing argument");

示例:使用 TF 子程序实现自定义任务

以下是一个简单的 Verilog 和 C 代码示例,展示如何使用 TF 子程序实现一个自定义系统任务 $print_count,打印计数器值和模块路径。

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 任务
    always @(posedge clk) begin
        $print_count(count);
    end
endmodule

module tb_test;
    reg clk, rst_n;
    wire [3:0] count;

    test uut (.clk(clk), .rst_n(rst_n), .count(count));

    initial begin
        clk = 0;
        rst_n = 0;
        #20 rst_n = 1;
        #100 $finish;
    end

    always #5 clk = ~clk; // 10ns 周期
endmodule

C 代码(print_count.c)

#include "pli.h" // 假设仿真器提供此头文件
#include <stdio.h>

// PLI 任务实现
int print_count_calltf(char *user_data) {
    int count_val = tf_getp(1); // 获取第一个参数(count)
    char *module_path = tf_mipname(); // 获取模块路径
    double sim_time = tf_gettime() / 1000.0; // 转换为 ns

    printf("Time: %.2f ns, Module: %s, Count: %d\n", 
           sim_time, module_path, count_val);
    return 0;
}

// PLI 注册函数
void print_count_register() {
    s_tfcell veriusertfs[] = {
        {usertask, 0, 0, 0, print_count_calltf, 0, "$print_count"},
        {0} // 结束标志
    };
    tf_setuptfs(veriusertfs);
}

pli.tab 文件

$print_count call=print_count_calltf acc=rw:%1

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

# 编译
vcs -full64 -P pli.tab test.v print_count.c -o simv
# 运行
./simv

输出示例

Time: 5.00 ns, Module: tb_test.uut, Count: 1
Time: 15.00 ns, Module: tb_test.uut, Count: 2
Time: 25.00 ns, Module: tb_test.uut, Count: 3
...

说明

  • Verilog 模块 test 是一个 4 位计数器。
  • 每次时钟上升沿,调用 $print_count(count)
  • C 代码使用 tf_getp 获取 count 值,tf_mipname 获取模块路径,tf_gettime 获取仿真时间。
  • 输出格式化时间、模块路径和计数器值。

扩展建议

  1. 多参数处理
  • 支持多个参数,检查参数数量:
    c if (tf_nump() < 2) tf_error("Expected 2 arguments"); int arg1 = tf_getp(1); int arg2 = tf_getp(2);
  1. 字符串参数
  • 处理字符串输入:
    verilog $print_count("Count is:", count);
    c char *prefix = tf_getcstringp(1); int count_val = tf_getp(2); printf("%s %d\n", prefix, count_val);
  1. 与前端交互
  • 将 TF 子程序的输出写入文件,供前端(如 JavaScript)读取:
    c FILE *fp = fopen("count.txt", "a"); fprintf(fp, "%.2f %s %d\n", tf_gettime() / 1000.0, tf_mipname(), tf_getp(1)); fclose(fp);
  1. 替代方案
  • 现代设计推荐使用 SystemVerilog DPI 替代 TF 子程序,接口更简单:
    verilog import "DPI-C" function void print_count(input int count); always @(posedge clk) print_count(count);
    c void print_count(int count) { printf("Count: %d\n", count); }

注意事项

  1. 不可综合
  • TF 子程序仅用于仿真,不能用于逻辑综合(如 Vivado、Quartus)。
  • 综合代码参考之前的可综合计数器或状态机示例。
  1. 工具依赖
  • TF 子程序实现因仿真器(VCS、ModelSim、QuestaSim)而异。
  • 确保正确配置 PLI 表和链接库。
  1. 性能
  • 频繁调用 TF 子程序可能降低仿真速度。
  • 优化:缓存参数值,减少 tf_getp 调用。
  1. 调试
  • 使用 tf_errortf_warning 报告问题。
  • 验证模块路径:tf_mipname

参考与资源

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

总结

TF 子程序是 Verilog PLI 的核心,用于仿真环境中处理系统任务/函数的参数和行为,适合测试平台和调试工具开发。上述 $print_count 示例展示了如何使用 tf_getptf_mipnametf_gettime 实现信号监控。TF 子程序不可综合,硬件设计需使用可综合 Verilog(如之前的计数器或 FSM 示例)。

如果需要更复杂的 TF 示例(如多参数处理、与前端交互或集成到特定仿真器),或想转向 SystemVerilog DPI,请提供具体需求!我也可以结合可综合 Verilog 代码,扩展为带 PLI 监控的完整测试平台。

类似文章

发表回复

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