草庐IT

Verilog中的任务Task和函数Function

Jobs-Wang 2024-03-01 原文

task和function说明语句分别用来定义任务和函数。

利用任务和函数可以把一个很大的程序模块分解成许多较小的任务和函数便于理解和调试。

输入、输出和总线信号的值可以传入、传出任务和函数。任务和函数往往还是大的程序模块中在不同地点多次用到的相同的程序段。

学会使用task和function语句可以简化程序的结构,使程序明白易懂,是编写较大型模块的基本功。

task和function说明语句的不同点

任务和函数有些不同,主要的不同有以下四点:

  • 1) 函数只能与主模块共用同一个仿真时间单位,而任务可以定义自己的仿真时间单位。
  • 2) 函数不能启动任务,而任务能启动其它任务和函数。
  • 3) 函数至少要有一个输入变量,而任务可以没有或有多个任何类型的变量。
  • 4) 函数返回一个值,而任务则不返回值。

函数的目的是通过返回一个值来响应输入信号的值。任务却能支持多种目的,能计算多个结果值,这些结果值只能通过被调用的任务的输出或总线端口送出。Verilog HDL模块使用函数时是把它当作表达式中的操作符,这个操作的结果值就是这个函数的返回值。下面让我们用例子来说明:

例如,定义一任务或函数对一个16位的字进行操作让高字节与低字节互换,把它变为另一个字(假定这个任务或函数名为: switch_bytes)。

任务返回的新字是通过输出端口的变量,因此16位字字节互换任务的调用源码是这样的:

 switch_bytes(old_word,new_word);

任务switch_bytes把输入old_word的字的高、低字节互换放入new_word端口输出。

而函数返回的新字是通过函数本身的返回值,因此16位字字节互换函数的调用源码是这样的:

 new_word = switch_bytes(old_word);

一 task语句

如果传给任务的变量值和任务完成后接收结果的变量已定义,就可以用一条语句启动任务。任务完成以后控制就传回启动过程。如任务内部有定时控制,则启动的时间可以与控制返回的时间不同。任务可以启动其它的任务,其它任务又可以启动别的任务,可以启动的任务数是没有限制的。不管有多少任务启动,只有当所有的启动任务完成以后,控制才能返回。

任务的定义

定义任务的语法如下:

任务:

task <任务名>;
     <端口及数据类型声明语句>
     <语句1>
     <语句2>
     .....
     <语句n>
endtask

这些声明语句的语法与模块定义中的对应声明语句的语法是一致的。

任务的调用及变量的传递

启动任务并传递输入输出变量的声明语句的语法如下:

任务的调用:

<任务名>(端口1,端口2,...,端口n);

下面的例子说明怎样定义任务和调用任务:

任务定义:

task my_task;
    input a, b;
    inout c;
    output d, e;
    …
    <语句> //执行任务工作相应的语句
    …
    c = foo1; //赋初始值
    d = foo2; //对任务的输出变量赋值t
    e = foo3;
endtask

任务调用:

my_task(v,w,x,y,z);

任务调用变量(v,w,x,y,z)和任务定义的I/O变量(a,b,c,d,e)之间是一一对应的。当任务启动时,由v,w,和x.传入的变量赋给了a,b,和c,而当任务完成后的输出又通过c,d和e赋给了x,y和z。

任务定义时需注意以下事项:

(1)在第一行“task”语句中不能列出端口名列表。

(2)任务中可以有延时语句、敏感事件控制语句等事件控制语句。

(3)任务可以没有或可以有一个或多个输入、输出和双向端口。

(4)任务可以没有返回值,也可以通过输出端口或双向端口返回一个或多个返回值。

(5)任务可以调用其它的任务或函数,也可以调用该任务本身。

(6)任务定义结构内不允许出现过程块(initial或always过程块)。

(7)任务定义结构内可以出现disable终止语句,这条语句的执行将中断正在执行的任务。在任务被中断后,程序流程将返回到调用任务的地方继续向下执行。

二 function语句

函数定义是嵌入在关键字functionendfunction之间的,其中关键词function标志着一个函数定义结构的开端,endfunction标志着一个函数定义结构的结束。

“<函数名>”是给被定义函数取的名称。这个函数名在函数定义结构内部还代表着一个内部变量,函数调用后的返回值是通过这个函数名变量传递给调用语句的。

函数的目的是返回一个用于表达式的值。

定义函数的语法:

function <返回值的类型或范围> (函数名);
<端口说明语句>
<变量类型说明语句>

begin
<语句>
........
end
endfunction

从函数返回的值

函数的定义蕴含声明了与函数同名的、函数内部的寄存器。如在函数的声明语句中<返回值的类型或范围>为缺省,则这个寄存器是一位的,否则是与函数定义中<返回值的类型或范围>一致的寄存器。

函数的定义把函数返回值所赋值寄存器的名称初始化为与函数同名的内部变量。

函数的调用

函数的调用是通过将函数作为表达式中的操作数来实现的。

其调用格式如下:

<函数名> (<表达式><,<表达式>>*)

其中函数名作为确认符。

函数的使用规则

与任务相比较函数的使用有较多的约束,下面给出的是函数的使用规则:

  • 1) 函数的定义不能包含有任何的时间控制语句,即任何用#、@、或wait来标识的语句。
  • 2) 函数不能启动任务。
  • 3) 定义函数时至少要有一个输入参量。
  • 4) 在函数的定义中必须有一条赋值语句给函数中的一个内部变量赋以函数的结果值,该内部变量具有和函数名相同的名字。

A.在进行函数定义时需要注意以下几点:

(1)与任务一样,函数定义结构只能出现在模块中,而不能出现在过程块内。

(2)函数至少必须有一个输入端口。

(3)函数不能有任何类型的输出端口(output端口)和双向端口(inout端口)。

(4)在函数定义结构中的行为语句部分内不能出现任何类型的时间控制描述,也不允许使用disable终止语句。

(5)与任务定义一样,函数定义结构内部不能出现过程块。

(6)在一个函数内可以对其它函数进行调用,但是函数不能调用其它任务。

(7)在第一行“function”语句中不能出现端口名列表。

(8)在函数声明的时候,在Verilog HDL的内部隐含地声明了一个名为function_identifier(函数标识符)的寄存器类型变量,函数的输出结果将通过这个寄存器类型变量被传递回来。

B.函数调用时要注意以下几点:

(1)函数的调用不能单独作为一条语句出现,它只能作为一个操作数出现在调用语句内。

(2)函数的调用既能出现在过程块中,也能出现在assign连续赋值语句中。

(3)函数定义中声明的所有局部寄存器都是静态的,即函数中的局部寄存器在函数的多个调用之间保持它们的值。

最后总结一下任务与函数的区别:

=========================================================================

示例:模块(内部定义了函数)

`timescale 1ns / 1ps
module reg_test(
    input clk,
    input [3:0] address,
    input [7:0] data_in,
    output [7:0]    data_out,
    input   write_en,
    input   read_en,
    input   resetb
);
    
reg [7:0]   reg0,reg1,reg2,reg3;
reg [7:0]   read_data;
//定义函数    
function    [7:0] dev_reg_nxt;
    input [3:0] address;
    input [3:0] reg_offset;
    input   write_en;
    input [7:0] data_in;
    input [7:0] dev_reg;
        begin
            dev_reg_nxt=(address==reg_offset && write_en)? data_in:dev_reg;
        end
endfunction
    
assign data_out=read_data;
always@(posedge clk or negedge resetb)begin
    if(!resetb)
        begin
            reg0<=8'b0000_0000;
            reg1<=8'b0000_0000;
            reg2<=8'b0000_0000;
            reg3<=8'b0000_0000;
            read_data<=8'b0000_0000;
        end
    else
        begin
            reg0<=dev_reg_nxt(address,4'b0000,write_en,data_in,reg0);//调用函数
            reg1<=dev_reg_nxt(address,4'b0001,write_en,data_in,reg1);
            reg2<=dev_reg_nxt(address,4'b0010,write_en,data_in,reg2);
            reg3<=dev_reg_nxt(address,4'b0011,write_en,data_in,reg3);
        end
end
        
always@(*)begin
    if(read_en)begin
        case(address)
            4'b0000:read_data=reg0;
            4'b0001:read_data=reg1;
            4'b0010:read_data=reg2;
            4'b0011:read_data=reg3;
        endcase
    end
end
endmodule

 上面模块的testbench(内部定义了任务)

`timescale 1ns / 1ps
module tstbench();

reg clk,write_en,read_en,resetb;
reg [3:0] address;
reg [7:0] data_in;
wire [7:0] data_out;  
     
reg_test test_function(
        .clk(clk),
        .address(address),
        .data_in(data_in),
        .data_out(data_out),
        .write_en(write_en),
        .read_en(read_en),
        .resetb(resetb)
        );
 
initial begin
     clk=1'b0;
     forever begin
        #5 clk=~clk;
     end 
end    

initial begin
    resetb=1'b1;
    #10    resetb=1'b0;
    #10    resetb=1'b1;
end

initial begin
    address=4'b0000;
    data_in=8'b0000_0000;
    write_en=1'b0;
    read_en=1'b0;
end
//定义任务,此任务执行完成占用三个时钟周期      
task reg_write;
    input [3:0] address_task;
    input [7:0] data_in_task;
    begin
        @(posedge clk);
            #1 address=address_task;
        @(posedge clk);
            #1 write_en=1'b1;
            data_in=data_in_task;
        @(posedge clk);
            #1;
            write_en=1'b0;
            address=4'hF;
            data_in=4'h0;     
    end
endtask
//定义任务,此个任务执行完成占用四个时钟周期 
task reg_read;
    input [3:0] address_task;
    input [7:0] expected_data;
    begin
        @(posedge clk);
                #1 address=address_task;
        @(posedge clk);
                #1 read_en=1'b1;
        @(posedge clk);
                #1 read_en=1'b0;
                address=4'hF;
        @(posedge clk);
                if(expected_data===data_out)
                $display("data match:expected_data=%h   ,actual data=%h   ",expected_data,data_out);
                else
                $display("ERROR !!! data nomatch:expected_data=%h   ,actual data=%h   ",expected_data,data_out);
    end
endtask

initial begin
#100;
        reg_write(4'b0000,8'hA5);//执行写任务
        reg_read(4'b0000,8'hA5);//执行读任务并与预期数据做比对       
        reg_write(4'b0001,8'hA2);
        reg_read(4'b0001,8'hA2);
        reg_write(4'b0010,8'hA3);
        reg_read(4'b0010,8'hA3);
        reg_write(4'b0011,8'hA1);
        reg_read(4'b0011,8'hA1);
//        $finish;
end
endmodule

以上的仿真波形: 

 

有关Verilog中的任务Task和函数Function的更多相关文章

  1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  2. ruby - 其他文件中的 Rake 任务 - 2

    我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

  3. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  4. ruby-on-rails - Rails 3 中的多个路由文件 - 2

    Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

  5. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

    我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

  6. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  7. ruby-on-rails - Rails 应用程序中的 Rails : How are you using application_controller. rb 是新手吗? - 2

    刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr

  8. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

    我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

  9. ruby - 如何使用 RSpec::Core::RakeTask 创建 RSpec Rake 任务? - 2

    如何使用RSpec::Core::RakeTask初始化RSpecRake任务?require'rspec/core/rake_task'RSpec::Core::RakeTask.newdo|t|#whatdoIputinhere?endInitialize函数记录在http://rubydoc.info/github/rspec/rspec-core/RSpec/Core/RakeTask#initialize-instance_method没有很好的记录;它只是说:-(RakeTask)initialize(*args,&task_block)AnewinstanceofRake

  10. ruby - rspec 需要 .rspec 文件中的 spec_helper - 2

    我注意到像bundler这样的项目在每个specfile中执行requirespec_helper我还注意到rspec使用选项--require,它允许您在引导rspec时要求一个文件。您还可以将其添加到.rspec文件中,因此只要您运行不带参数的rspec就会添加它。使用上述方法有什么缺点可以解释为什么像bundler这样的项目选择在每个规范文件中都需要spec_helper吗? 最佳答案 我不在Bundler上工作,所以我不能直接谈论他们的做法。并非所有项目都checkin.rspec文件。原因是这个文件,通常按照当前的惯例,只

随机推荐