Verilog Tutorial(9)任务Task与函数Function的使用

本文将讨论如何在 verilog 中使用任务task与函数function。总的来说,task和function也被称为子程序(subprograms ),因为它们允许设计者编写可复用的verilog 代码。

与大多数编程语言一样,设计者应该尽可能编写可复用性强的 verilog 代码,这能够有效地减少未来项目的开发时间----可以更轻松地将代码从一个项目移植到另一个项目。

任务task与函数function之间有两个主要的区别。编写函数时,它会执行计算并返回单个值。相反,任务则会执行许多顺序语句且不返回值,但任务可以有无限数量的输出。函数会立即执行,并且不能包含任何时间控制语句,例如delay、posedge 或wait等语句;任务则可以包含时间控制语句。

函数Function

在 verilog 中,函数是一个子程序,它可以由一个或多个输入值,运行一些计算并返回一个输出值。

在设计中的多个地方使用的相同代码的这一操作可以使用函数来替代,通过使用一个函数而不是在多个地方重复相同的代码,可以使代码更易于维护。

下面的代码片段展示了 verilog 中函数的一般语法。

//第一种方法
function <return_type> <name> (input <arguments>);
  // Declaration of local variables
  begin
    // function code
  end
endfunction

//第二种方法
function <return_type> <name>;
  (input <arguments>);
  // Declaration of local variables
  begin
    // function code
  end
endfunction

每个函数都必须有一个名称,如上例中的 <name> 所示。

设计者可以将输入声明与函数声明写在同一行(方法1),或者输入声明也可以作为函数体的一部分。用来声明输入参数的方法对函数的性能没有影响。

将输入声明与函数声明写在同一行时,也可以省略 begin 和 end 关键字。

在上面的示例中, <arguments> 被用来来声明函数的输入。

<return_type> 被用来声明函数返回值的数据类型,如果在函数声明中排除这部分,那么该函数将默认返回一个 1 位的值。

当返回一个值时,通过给函数名赋值的方式来实现。下面的代码片段展示了如何简单地将输入返回给函数。

function integer easy_example (input integer a);
  easy_example = a;
endfunction

函数使用规则

尽管函数通常很简单,但在编写 verilog 函数时必须遵循一些基本规则。

函数一个最重要的规则是它们不能包含任何时间控制语句,例如delay、posedge 或wait等语句。当设计者想要编写一个带有时间控制的子程序时,应该改用任务。

因此设计者也无法从函数内调用任务,相反却可以从函数内调用另一个函数。

由于函数会立即执行,所以只能在函数中使用阻塞赋值。

在编写函数的时候,可以声明和使用局部变量,这意味着可以在函数中声明不能在声明它的函数之外访问的变量。除此之外,还可以在函数中访问所有全局变量。

例如,如果在一个模块中声明一个函数,那么该函数可以访问和修改该模块中声明的所有变量。

下表总结了在 verilog 中使用函数的规则。

在 Verilog 中使用函数的规则

函数可以有一个或多个输入参数

函数只能返回一个值

不能从函数中调用任务,可以从函数中调用其他函数

函数内不能使用非阻塞赋值

局部变量可以在函数内部声明和使用

可以从函数内部访问和修改全局变量

如果不指定返回类型,该函数将返回一个bit

函数示例

为了更好地演示如何使用 verilog 函数,请看一个基本示例:编写一个函数,它接受 2 个输入参数并返回它们的和。

除了使用integer类型作为输入参数和返回类型外,还必须使用加法运算符来计算输入的总和。

下面的代码片段展示了此示例函数在 verilog 中的实现。可以使用两种方法来声明 verilog 函数,这两种方法都显示在下面的代码中。

//方法1
function integer addition (input integer in_a, in_b);
  addition = in_a + in_b;
endfunction

//方法2
function integer addition;
  input integer in_a;
  input integer in_b;
  begin
    addition = in_a + in_b;
  end
endfunction

调用函数

当设计者想在verilog 设计的另一部分中使用函数时,就必须调用它。用于执行此操作的方法类似于其他编程语言。

调用一个函数时,设计者用与声明它们的顺序相同的顺序将参数传递给函数,这被称为位置关联(positional association),这意味着声明参数的顺序非常重要。

下面的代码片段展示了如何使用位置关联的方法来调用加法示例函数。

在下面的示例中,in_a 将映射到 a 参数,in_b 将映射到 b。

//函数调用
func_out = addition(a, b);

Automatic 函数

设计者还可以使用 verilog automatic 关键字将函数声明为可重入的(reentrant)。automatic 关键字是在Verilog-2001标准中被引入的,这意味着不能在使用Verilog-1995标准时编写可重入函数。

将函数声明为可重入时,函数内的变量和参数是动态分配的。相反,普通函数的内部变量和参数是静态分配的。

编写一个普通函数时,所有用于执行函数处理的内存都只分配一次。这个过程在计算机科学中被称为静态内存分配。因此,仿真软件必须完整地执行该功能,然后才能再次使用该功能。这也意味着函数使用的内存永远不会被释放(deallocated)。因此,存储在此内存中的任何值将在函数调用之间保持它们的值。

相反,使用 automatic 关键字的函数会在调用函数时分配内存。一旦函数完成,内存就会被释放。此过程在计算机科学中称为自动或动态内存分配。因此,仿真软件可以实现automatic函数的多个实例。

设计者可以使用automatic关键字在verilog中编写递归函数(recursive functions),这意味着可以创建调用自身来执行计算的函数。

例如,递归函数的一个常见用例是计算给定数字的阶乘。

下面的代码片段展示了如何使用 automatic 关键字在 verilog 中编写递归函数。

function automatic integer factorial (input integer a);
  begin
    if (a > 1) begin
      factorial = a * factorial(a - 1);
    end
    else begin
      factorial = 1;
    end
  end
endfunction

任务Task

就像函数一样,任务可以用来替代在整个设计中重复使用的一小段代码。任务可以有任意数量的输入,也可以生成任意数量的输出。这与只能返回单个值的函数形成对比。

与函数不同,在任务中还可以使用时间控制语句,例如 wait、posedge 或 delays (#)。因此,在任务中同时使用阻塞赋值和非阻塞赋值。这些特性意味着任务最适合用于实现在设计中重复多次的简单代码片段。

设计者可以创建由特定文件中的所有模块共享的全局任务,为此只需在文件中的模块声明之外编写任务代码。

下面的代码片段展示了任务的一般语法。

//方法1
task <name> (<io_list>);
  begin
    //实现任务的代码
  end
endtask

//方法2
task <name>;
  <io_list>
  begin
    //实现任务的代码
  end
endtask

与函数一样,任务也可以通过两种方式声明,但两种方式的效果是相同的。

每个任务都必须有一个名称,如上面的 <name> 所示。

当编写在任务主体中声明输入和输出的任务时,还必须使用 begin 和 end 关键字。但是,当对输入和输出使用内联声明(inline declaration)时,可以省略 begin 和 end 关键字。

在编写任务时,可以声明和使用局部变量,这意味着可以在任务中创建变量,这些变量不能在声明它的任务之外访问。除此之外也可以访问任务中的所有全局变量。

与函数不同,任务中可以调用任务和函数。

任务示例

让考虑一个简单的示例,以更好地演示如何编写任务。为此将编写一个可用于生成脉冲的基本任务。调用任务就可以指定脉冲的长度。

为此,需要一个输入(确定脉冲的长度)和一个用于生成脉冲的输出。

下面的 verilog 代码展示了使用两种不同风格的任务来实现这个例子。

//方法1
task pulse_generate(input time pulse_length, output pulse);
  pulse = 1'b1
  #pulse_time
  pulse = 1'b0;
endtask
 
//方法2
task pulse_generate;
  input time pulse_length;
  output pulse;
  begin
    pulse = 1'b1;
    #pulse_time
    pulse = 1'b0;
  end
endtask

虽然这个示例很简单,但在这里看到如何在任务中使用时延运算符 (#)。如果在一个函数中编写这段代码,那么肯定会编译错误。

从这个例子中也可以看出,这时并没有像处理函数那样有返回值。相反,必须声明在任务声明中使用的任何输出。编写任务时可以包含和驱动任意数量的输出。

调用任务

调用任务的方法类似于用于调用函数的方法。但是有一个重要区别----调用任务时,不能像使用函数那样将其用作表达式的一部分。相反,应该将任务调用视为将代码块打包到设计中的一种简便方法。

与函数一样,调用任务时使用位置关联方法将参数传递给任务,这意味着将参数传递给任务的顺序与在编写任务代码时声明它们的顺序要相同。

下面的代码片展示了如何使用位置关联方法来调用之前的 pulse_generate 任务。在这种情况下,pulse_length 输入映射到 pulse_time 变量,脉冲输出映射到 pulse_out 变量。

generate_pulse(pulse_time, pulse_out);

Automatic任务

设计者还可以使用 verilog automatic 关键字将任务声明为可重入的(reentrant)。automatic 关键字是在Verilog-2001标准中被引入的,这意味着不能在使用Verilog-1995标准时编写可重入任务。

使用 automatic 关键字意味着仿真工具将使用动态内存分配。与函数一样,任务默认使用静态内存分配,这意味着仿真工具只能运行任务的一个实例。相反,使用 automatic 关键字的任务会在任务被调用时分配内存。一旦任务完成,内存就会被释放。

请看一个基本示例来展示automatic任务的使用以及它们与普通任务的区别。这个例子将使用一个简单的任务,将局部变量的值增加给定的数量,然后在模拟工具中多次运行它,以查看局部变量在使用automatic任务和普通任务时的区别。

下面的代码展示了如何编写一个静态任务来实现这个例子。

task increment(input integer incr);
  integer i = 1;
  i = i + incr;
  $display("Result of increment = %0d", i);
endtask

//运行3次
initial begin
  increment(1);
  increment(2);
  increment(3);
end

在仿真工具中运行此代码会产生以下输出:

Result of increment = 2
Result of increment = 4
Result of increment = 7

从这里可以看出,局部变量 i 的值是静态的,并且存储在一个单独的内存位置。因此,i 的值是不变的(persistent),并且在任务调用之间保持它的值。

调用任务时,正在增加已经存储在给定内存位置的值。

下面的代码片段显示了相同的任务,只是这次使用了 automatic 关键字。

task automatic increment(input integer incr);
  integer i = 1;
  i = i + incr;
  $display("Result of increment = %0d", i);
endtask

//运行3次
initial begin
  increment(1);
  increment(2);
  increment(3);
end

仿真工具中运行此代码会产生以下输出:

Result of increment = 2
Result of increment = 3
Result of increment = 4

从这里可以看到局部变量 i 是如何变成动态的,其在调用任务时创建。在它被创建之后,它被赋值为 1。当任务完成运行时,动态分配的内存被释放,局部变量不再存在。


  • 📣您有任何问题,都可以在评论区和我交流📃

  • 📣本文由 孤独的单刀 原创,首发于CSDN平台🐵,博客主页:wuzhikai.blog.csdn.net

  • 📣您的支持是我持续创作的最大动力!如果本文对您有帮助,还请多多点赞👍、评论💬和收藏


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/1656.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深入理解WebSocket协议

“ 一直以来对WebSocket仅停留在使用阶段&#xff0c;也没有深入理解其背后的原理。当看到 x x x was not upgraded to websocket&#xff0c;我是彻底蒙了&#xff0c;等我镇定下来&#xff0c;打开百度输入这行报错信息&#xff0c;随即看到的就是大家说的跨域&#xff0c;或…

SpringBoot帮你优雅的关闭WEB应用程序

Graceful shutdown 应用 Graceful shutdown说明 Graceful shutdown is supported with all four embedded web servers (Jetty, Reactor Netty, Tomcat, and Undertow) and with both reactive and servlet-based web applications. It occurs as part of closing the applica…

spring(七):事务操作

spring&#xff08;七&#xff09;&#xff1a;事务操作前言一、什么是事务二、事务四个特性&#xff08;ACID&#xff09;三、事务操作&#xff08;搭建事务操作环境&#xff09;四、事务操作&#xff08;Spring 事务管理介绍&#xff09;五、事务操作&#xff08;注解声明式事…

python学习——【第一弹】

前言 Python是一种跨平台的计算机程序设计语言&#xff0c;是ABC语言的替代品&#xff0c;属于面向对象的动态类型语言&#xff0c;最初被设计用于编写自动化脚本&#xff0c;随着版本的不断更新和语言新功能的添加&#xff0c;越来越多被用于独立的、大型项目的开发。 从这篇…

断言assert

assert作用&#xff1a;我们使用assert这个宏来调试代码语法&#xff1a;assert&#xff08;bool表达式&#xff09;如果表达式为false&#xff0c;会调用std::cout<<abort函数&#xff0c;弹出对话框&#xff0c;#include<iostream> #include<cassert> void…

学习 Python 之 Pygame 开发魂斗罗(八)

学习 Python 之 Pygame 开发魂斗罗&#xff08;八&#xff09;继续编写魂斗罗1. 创建敌人类2. 增加敌人移动和显示函数3. 敌人开火4. 修改主函数5. 产生敌人6. 使敌人移动继续编写魂斗罗 在上次的博客学习 Python 之 Pygame 开发魂斗罗&#xff08;七&#xff09;中&#xff0…

uboot主目录下Makefile文件的分析,以及配置过程分析

主Makefile执行分析 uboot的编译过程 &#xff08;1&#xff09;配置 查看主Makefile文件下所支持的配置的板子&#xff0c;通过make x210_sd_config来实现编译前的配置 &#xff08;2&#xff09;编译 make直接编译&#xff0c;这个前提条件是主Makefile文件下指定了编译…

上手使用百度文心一言

3月16日&#xff0c;在距离新一代的GPT模型GPT-4发布还不足一天的时间内&#xff0c;百度便发布了对标ChatGPT的人工智能产品&#xff0c;名字叫&#xff1a;文心一言。成为国内首页发布该类型产品的公司。 那么&#xff0c;我们今天就来试一试百度的文心一言好不好用。 首先&a…

【ERNIE Bot】百度 | 文心一言初体验

文章目录一、前言二、文心一言介绍三、申请体验⌈文心一言⌋四、⌈文心一言⌋初体验1️⃣聊天对话能力2️⃣文案创作能力3️⃣文字转语音能力✨4️⃣AI绘画能力✨5️⃣数理推理能力6️⃣代码生成能力7️⃣使用技巧说明五、总结一、前言 ​ 最近有关人工智能的热门话题冲上热榜…

Java课程设计项目--音乐视频网站系统

一、功能介绍 随着社会的快速发展&#xff0c;计算机的影响是全面且深入的。人们生活水平的不断提高&#xff0c;日常生活中人们对音乐方面的要求也在不断提高&#xff0c;听歌的人数更是不断增加&#xff0c;使得音乐网站的设计的开发成为必需而且紧迫的事情。音乐网站的设计主…

「操作系统」什么是用户态和内核态?为什么要区分

「操作系统」什么是用户态和内核态&#xff1f;为什么要区分 参考&鸣谢 从根上理解用户态与内核态 程序员阿星 并发编程&#xff08;二十六&#xff09;内核态和用户态 Lovely小猫 操作系统之内核态与用户态 fimm 文章目录「操作系统」什么是用户态和内核态&#xff1f;为什…

嵌入式硬件电路设计的基本技巧

目录 1 分模块 2 标注关键参数 3 电阻/电容/电感/磁珠的注释 4 可维修性 5 BOM表归一化 6 电源和地的符号 7 测试点 8 网络标号 9 容错性/兼容性 10 NC、NF 11 版本变更 12 悬空引脚 13 可扩展性 14 防呆 15 信号的流向 16 PCB走线建议 17 不使用\表示取反 不…

考研408每周一题(2019 41)

2019年(单链表&#xff09; 41.(13分)设线性表L(a1,a2,a3,...,a(n-2),a(n-1),an)采用带头结点的单链表保存&#xff0c;链表中的结点定义如下&#xff1a; typedef struct node {int data;struct node *next; } NODE; 请设计一个空间复杂度为O(1)且时间上尽可能高效的算法&…

leetcode -- 876.链表的中间节点

文章目录&#x1f428;1.题目&#x1f407;2. 解法1-两次遍历&#x1f340;2.1 思路&#x1f340;2.2 代码实现&#x1f401;3. 解法2-快慢指针&#x1f33e;3.1 思路&#x1f33e;3.2 **代码实现**&#x1f42e;4. 题目链接&#x1f428;1.题目 给你单链表的头结点head&#…

RocketMQ

RocketMQ1、基础入门1、消息中间件(MQ)的定义2、为什么要用消息中间件&#xff1f;2、RocketMQ 产品发展1、RocketMQ 版本发展2、RocketMQ 的物理架构1、核心概念2、物理架构中的整体运转3、RocketMQ 的概念模型1、分组(Group)2、主题(Topic)3、标签(Tag)4、消息队列(Message Q…

开发也可以很快乐,让VSCode和CodeGPT带给你幸福感

CodeGPT 是一款 Visual Studio Code 扩展&#xff0c;可以通过官方的 OpenAI API 使用 GPT-3 (预训练生成式转换器) 模型&#xff0c;在多种编程语言中生成、解释、重构和文档化代码片段。CodeGPT 可用于各种任务&#xff0c;例如代码自动完成、生成和格式化。它还可以集成到代…

smartsofthelp最简单的,最好的,最干净的C# 代码生成器

关系型数据库高并发接口代码生成EF API 接口原声SQL 操作类异步委托 await 操作数据库数据异步访问抽象基础类 netcore 生成EF ORMdbhelperasync原生SQL 异步数据库操作公共类自动生成增删改查成员方法实例代码#region 自动生成增删改查成员方法/// <summary>/// 增加一条…

【6】核心易中期刊推荐——图像与信号处理

🚀🚀🚀NEW!!!核心易中期刊推荐栏目来啦 ~ 📚🍀 核心期刊在国内的应用范围非常广,核心期刊发表论文是国内很多作者晋升的硬性要求,并且在国内属于顶尖论文发表,具有很高的学术价值。在中文核心目录体系中,权威代表有CSSCI、CSCD和北大核心。其中,中文期刊的数…

ChatGPT-4.0 : 未来已来,你来不来

文章目录前言ChatGPT 3.5 介绍ChatGPT 4.0 介绍ChatGPT -4出逃计划&#xff01;我们应如何看待ChatGPT前言 好久没有更新过技术文章了&#xff0c;这个周末听说了一个非常火的技术ChatGPT 4.0&#xff0c;于是在闲暇之余我也进行了测试&#xff0c;今天这篇文章就给大家介绍一…

【Bezier + BSpline + CatmullRom】移动机器人曲线路径规划

问题&#xff1a;现有n1n1n1个2维的离散点Pi(xi,yi),(i0,1,⋯,n){P_i} \left( {{x_i},{y_i}} \right),\left( {i 0,1, \cdots ,n} \right)Pi​(xi​,yi​),(i0,1,⋯,n), 如何用Pi{P_i}Pi​拟合一条平滑的曲线&#xff0c;最后将曲线分割成数条 2阶/3阶贝塞尔曲线&#xff0c;…
最新文章