• Verilog 函数


            在 Verilog 中,可以利用任务(关键字为 task)或函数(关键字为 function),将重复性的行为级设计进行提取,并在多个地方调用,来避免重复代码的多次编写,使代码更加的简洁、易懂。

    函数

            函数只能在模块中定义,位置任意,并在模块的任何地方引用,作用范围也局限于此模块。函数主要有以下几个特点:

    • 1)不含有任何延迟、时序或时序控制逻辑
    • 2)至少有一个输入变量
    • 3)只有一个返回值,且没有输出
    • 4)不含有非阻塞赋值语句
    • 5)函数可以调用其他函数,但是不能调用任务

    Verilog 函数声明格式如下:

    1. function [range-1:0] function_id ;
    2. input_declaration ;
    3. other_declaration ;
    4. procedural_statement ;
    5. endfunction

            函数在声明时,会隐式的声明一个宽度为 range、 名字为 function_id 的寄存器变量,函数的返回值通过这个变量进行传递。当该寄存器变量没有指定位宽时,默认位宽为 1。函数通过指明函数名与输入变量进行调用。函数结束时,返回值被传递到调用处。

    函数调用格式如下:

    function_id(input1, input2, …);

    下面用函数实现一个数据大小端转换的功能。当输入为 4'b0011 时,输出可为 4'b1100。例如:

    1. module endian_rvs
    2. #(parameter N = 4)
    3. (
    4. input en, //enable control
    5. input [N-1:0] a ,
    6. output [N-1:0] b
    7. );
    8. reg [N-1:0] b_temp ;
    9. always @(*) begin
    10. if (en) begin
    11. b_temp = data_rvs(a);
    12. end
    13. else begin
    14. b_temp = 0 ;
    15. end
    16. end
    17. assign b = b_temp ;
    18. //function entity
    19. function [N-1:0] data_rvs ;
    20. input [N-1:0] data_in ;
    21. parameter MASK = 32'h3 ;
    22. integer k ;
    23. begin
    24. for(k=0; k
    25. data_rvs[N-k-1] = data_in[k] ;
    26. end
    27. end
    28. endfunction
    29. endmodule

    函数里的参数也可以改写,例如:

    defparam data_rvs.MASK = 32'd7 ;

            但是仿真时发现,此种写法编译可以通过,仿真结果中,函数里的参数 MASK 实际并没有改写成功,仍然为 32'h3。这可能和编译器有关,有兴趣的学者可以用其他 Verilog 编译器进行下实验。函数在声明时,也可以在函数名后面加一个括号,将 input 声明包起来。

    例如上述大小端声明函数可以表示为:

    1. function [N-1:0] data_rvs(
    2. input [N-1:0] data_in
    3. ......
    4.     ) ;

    常数函数

            常数函数是指在仿真开始之前,在编译期间就计算出结果为常数的函数。常数函数不允许访问全局变量或者调用系统函数,但是可以调用另一个常数函数。这种函数能够用来引用复杂的值,因此可用来代替常量。

    例如下面一个常量函数,可以来计算模块中地址总线的宽度:

    1. parameter    MEM_DEPTH = 256 ;
    2. reg  [logb2(MEM_DEPTH)-10] addr ; //可得addr的宽度为8bit
    3.  
    4.     function integer     logb2;
    5.     input integer     depth ;
    6.         //2569bit,我们最终数据应该是8,所以需depth=2时提前停止循环
    7.     for(logb2=0; depth>1; logb2=logb2+1) begin
    8.         depth = depth >> 1 ;
    9.     end
    10. endfunction

    automatic 函数

            在 Verilog 中,一般函数的局部变量是静态的,即函数的每次调用,函数的局部变量都会使用同一个存储空间。若某个函数在两个不同的地方同时并发的调用,那么两个函数调用行为同时对同一块地址进行操作,会导致不确定的函数结果。

            Verilog 用关键字 automatic 来对函数进行说明,此类函数在调用时是可以自动分配新的内存空间的,也可以理解为是可递归的。因此,automatic 函数中声明的局部变量不能通过层次命名进行访问,但是 automatic 函数本身可以通过层次名进行调用。

    下面用 automatic 函数,实现阶乘计算

    1. wire [31:0]          results3 = factorial(4);
    2. function automatic   integer         factorial ;
    3.     input integer     data ;
    4.     integer           i ;
    5.     begin
    6.         factorial = (data>=2)? data * factorial(data-1) : 1 ;
    7.     end
    8. endfunction // factorial

  • 相关阅读:
    d中nan接下来
    圆锥药型罩采用2D、3D单层和3D三种方法侵彻结果对比(基于LS-DYNA软件平台)
    OpenGL_Learn15(投光物)
    JAVA计算机毕业设计校园网络维修系统Mybatis+源码+数据库+lw文档+系统+调试部署
    使用 SAP UI5 ABAP Repository 部署本地 SAP UI5 应用到 ABAP 服务器的单步调试
    C#经典十大排序算法(完结)
    openEuler生态大会 | 麒麟信安受邀参加欧拉伙伴入驻仪式,加速欧拉新发展
    leetcode top100 (9)找到字符串中所有字母异位词
    微信小程序 - 小程序二维码迷之开发版
    文件服务之nfs
  • 原文地址:https://blog.csdn.net/qq_33300585/article/details/127826778