• Verilog task使用说明


    任务与函数的区别

    和函数一样,任务(task)可以用来描述共同的代码段,并在模块内任意位置被调用,让代码更加的直观易读。函数一般用于组合逻辑的各种转换和计算,而任务更像一个过程,不仅能完成函数的功能,还可以包含时序控制逻辑。下面对任务与函数的区别进行概括:

    比较点函数任务
    输入函数至少有一个输入,端口声明不能包含 inout 型任务可以没有或者有多个输入,且端口声明可以为 inout 型
    输出函数没有输出任务可以没有或者有多个输出
    返回值函数至少有一个返回值

    任务没有返回值

    仿真时刻函数总在零时刻就开始执行任务可以在非零时刻执行
    时序逻辑函数不能包含任何时序控制逻辑任务不能出现 always 语句,但可以包含其他时序控制,如延时语句
    调用函数只能调用函数,不能调用任务任务可以调用函数和任务
    书写规范函数不能单独作为一条语句出现,只能放在赋值语言的右端任务可以作为一条单独的语句出现语句块中

    任务

    任务声明

    任务在模块中任意位置定义,并在模块内任意位置引用,作用范围也局限于此模块。

    模块内子程序出现下面任意一个条件时,则必须使用任务而不能使用函数。

    • 1)子程序中包含时序控制逻辑,例如延迟,事件控制等
    • 2)没有输入变量
    • 3)没有输出或输出端的数量大于 1

    Verilog 任务声明格式如下:

    1. task task_id ;
    2. port_declaration ;
    3. procedural_statement ;
    4. endtask

            任务中使用关键字 input、output 和 inout 对端口进行声明。input 、inout 型端口将变量从任务外部传递到内部,output、inout 型端口将任务执行完毕时的结果传回到外部。进行任务的逻辑设计时,可以把 input 声明的端口变量看做 wire 型,把 output 声明的端口变量看做 reg 型。但是不需要用 reg 对 output 端口再次说明。对 output 信号赋值时也不要用关键字 assign。为避免时序错乱,建议 output 信号采用阻塞赋值。

    例如,一个带延时的异或功能 task 描述如下:

    1. task xor_oper_iner;
    2. input [N-1:0] numa;
    3. input [N-1:0] numb;
    4. output [N-1:0] numco ;
    5. //output reg [N-1:0] numco ; //无需再注明 reg 类型,虽然注明也可能没错
    6. #3 numco = numa ^ numb ;
    7. //assign #3 numco = numa ^ numb ; //不用assign,因为输出默认是reg
    8. endtask

    任务在声明时,也可以在任务名后面加一个括号,将端口声明包起来。

    上述设计可以更改为:

    1. task xor_oper_iner(
    2. input [N-1:0] numa,
    3. input [N-1:0] numb,
    4. output [N-1:0] numco ) ;
    5. #3 numco = numa ^ numb ;
    6. endtask

    任务调用

    任务可单独作为一条语句出现在 initial 或 always 块中,调用格式如下:

    task_id(input1, input2, …,outpu1, output2, …);

            任务调用时,端口必须按顺序对应。输入端连接的模块内信号可以是 wire 型,也可以是 reg 型。输出端连接的模块内信号要求一定是 reg 型,这点需要注意。

    对上述异或功能的 task 进行一个调用,完成对异或结果的缓存。

    1. module xor_oper
    2. #(parameter N = 4)
    3. (
    4. input clk ,
    5. input rstn ,
    6. input [N-1:0] a ,
    7. input [N-1:0] b ,
    8. output [N-1:0] co );
    9. reg [N-1:0] co_t ;
    10. always @(*) begin //任务调用
    11. xor_oper_iner(a, b, co_t);
    12. end
    13. reg [N-1:0] co_r ;
    14. always @(posedge clk or negedge rstn) begin
    15. if (!rstn) begin
    16. co_r <= 'b0 ;
    17. end
    18. else begin
    19. co_r <= co_t ; //数据缓存
    20. end
    21. end
    22. assign co = co_r ;
    23. /*------------ task -------*/
    24. task xor_oper_iner;
    25. input [N-1:0] numa;
    26. input [N-1:0] numb;
    27. output [N-1:0] numco ;
    28. #3 numco = numa ^ numb ; //阻塞赋值,易于控制时序
    29. endtask
    30. endmodule

    对上述异或功能设计进行简单的仿真,testbench 描述如下。

    激励部分我们使用简单的 task 进行描述,激励看起来就更加的清晰简洁。

    其实,task 最多的应用场景还是应用于 testbench 中进行仿真。task 在一些编译器中也不支持综合。

    1. `timescale 1ns/1ns
    2. module test ;
    3. reg clk, rstn ;
    4. initial begin
    5. rstn = 0 ;
    6. #8 rstn = 1 ;
    7. forever begin
    8. clk = 0 ; # 5;
    9. clk = 1 ; # 5;
    10. end
    11. end
    12. reg [3:0] a, b;
    13. wire [3:0] co ;
    14. initial begin
    15. a = 0 ;
    16. b = 0 ;
    17. sig_input(4'b1111, 4'b1001, a, b);
    18. sig_input(4'b0110, 4'b1001, a, b);
    19. sig_input(4'b1000, 4'b1001, a, b);
    20. end
    21. task sig_input ;
    22. input [3:0] a ;
    23. input [3:0] b ;
    24. output [3:0] ao ;
    25. output [3:0] bo ;
    26. @(posedge clk) ;
    27. ao = a ;
    28. bo = b ;
    29. endtask ; // sig_input
    30. xor_oper u_xor_oper
    31. (
    32. .clk (clk ),
    33. .rstn (rstn ),
    34. .a (a ),
    35. .b (b ),
    36. .co (co ));
    37. initial begin
    38. forever begin
    39. #100;
    40. if ($time >= 1000) $finish ;
    41. end
    42. end
    43. endmodule // test

    任务操作全局变量

            因为任务可以看做是过程性赋值,所以任务的 output 端信号返回时间是在任务中所有语句执行完毕之后。任务内部变量也只有在任务中可见,如果想具体观察任务中对变量的操作过程,需要将观察的变量声明在模块之内、任务之外,可谓之"全局变量"。

    例如有以下 2 种尝试利用 task 产生时钟的描述方式。

    1. //way1 to decirbe clk generating, not work
    2. task clk_rvs_iner ;
    3. output clk_no_rvs ;
    4. # 5 ; clk_no_rvs = 0 ;
    5. # 5 ; clk_no_rvs = 1 ;
    6. endtask
    7. reg clk_test1 ;
    8. always clk_rvs_iner(clk_test1);
    9. //way2: use task to operate global varialbes to generating clk
    10. reg clk_test2 ;
    11. task clk_rvs_global ;
    12. # 5 ; clk_test2 = 0 ;
    13. # 5 ; clk_test2 = 1 ;
    14. endtask // clk_rvs_iner
    15. always clk_rvs_global;

    automatic 任务

            和函数一样,Verilog 中任务调用时的局部变量都是静态的。可以用关键字 automatic 来对任务进行声明,那么任务调用时各存储空间就可以动态分配,每个调用的任务都各自独立的对自己独有的地址空间进行操作,而不影响多个相同任务调用时的并发执行。如果一任务代码段被 2 处及以上调用,一定要用关键字 automatic 声明。

    当没有使用 automatic 声明任务时,任务被 2 次调用,可能出现信号间干扰,例如下面代码描述:

    1. task test_flag ;
    2. input [3:0] cnti ;
    3. input en ;
    4. output [3:0] cnto ;
    5. if (en) cnto = cnti ;
    6. endtask
    7. reg en_cnt ;
    8. reg [3:0] cnt_temp ;
    9. initial begin
    10. en_cnt = 1 ;
    11. cnt_temp = 0 ;
    12. #25 ; en_cnt = 0 ;
    13. end
    14. always #10 cnt_temp = cnt_temp + 1 ;
    15. reg [3:0] cnt1, cnt2 ;
    16. always @(posedge clk) test_flag(2, en_cnt, cnt1); //task(1)
    17. always @(posedge clk) test_flag(cnt_temp, !en_cnt, cnt2);//task(2)

  • 相关阅读:
    beego框架自学笔记1
    【Java异常易错点】try或catch语句块中return后,finally还会执行吗?
    MyBtis 替换符号 查询 SQL 代替符号
    《HelloGitHub》第 89 期
    基于gunicorn部署flask项目
    【正点原子STM32连载】 第二十九章 低功耗实验 摘自【正点原子】MiniPro STM32H750 开发指南_V1.1
    qt day2
    C++ 学习笔记
    软件报错msvcr120.dll丢失怎么办?五个有效修复方法分享
    E: Unable to locate package xxxx
  • 原文地址:https://blog.csdn.net/qq_33300585/article/details/127759958