前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Verilog Task Concurrent Activation

Verilog Task Concurrent Activation

作者头像
icsoc
发布2020-09-30 10:46:41
7400
发布2020-09-30 10:46:41
举报
文章被收录于专栏:ICSOC.TECHICSOC.TECH

旧文排版重发。


Verilog Task 的并发执行机制实验和分析。

最近做一个模块级的仿真,需要在两个过程中反复调用同一个 Task。这种场景还是比较常见的,比如一个过程作为普通的配置过程,一个作为中断服务过程,这个 Task 可以是寄存器读或写。

在某些 Case 下,发现该 Task 的防止并发执行的逻辑并不能正常工作。

于是做了些阅读和实验,弥补了一下自己在 Task 并发执行上的知识漏洞。

Static Task

先来个不符合预期的例程:

代码语言:javascript
复制
module tb;
  reg           test_busy;
  integer       test_num;

  initial begin
    #100;
    test(1);
  end

  initial begin
    #200;
    test(2);
  end

  initial begin
    #300;
    test(3);
  end

  initial begin
    #400;
    test(4);
  end

  initial begin
    test_busy = 1'b0;
    test_num  = 0;
    #10000;
    $finish;
  end

  task test;
    input [31:0]  id;
    $display($time, "\t task <%0d> wait ...", id);
    wait(test_busy == 1'b0);
    test_busy = 1'b1;
    $display($time, "\t task <%0d> run  ...", id);
    #1000;
    test_num = test_num + 1;
    $display($time, "\t task <%0d> end  ...<%0d>", id, test_num);
    test_busy = 1'b0;
  endtask
  
  initial begin
    $fsdbAutoSwitchDumpfile( 500, "test.fsdb", 10 );
    $fsdbDumpvars(0, tb);
    $fsdbDumpSVA(0, tb);
  end

endmodule //tb

其运行结果为:

代码语言:javascript
复制
 100   task <1> wait ...
 100   task <1> run  ...
 200   task <2> wait ...
 300   task <3> wait ...
 400   task <4> wait ...
1100   task <4> end  ...<1>
1100   task <4> run  ...
2100   task <4> end  ...<2>
2100   task <4> run  ...
3100   task <4> end  ...<3>
3100   task <4> run  ...
4100   task <4> end  ...<4>

从运行结果可以看到,task<1>、task<2>、task<3> 均没有正常结束,都被 task<4> 覆盖掉了。什么原因呢?

在 Verilog 标准 IEEE.1363-2005 里有这样的解释:

All variables of a static task shall be static in that there shall be a single variable corresponding to each declared local variable in a module instance, regardless of the number of concurrent activations of the task. Variables declared in static tasks, including input, output, and inout type arguments, shall retain their values between invocations.

对应上述的例程,就是在静态 task test 中,输入 id 这个参数是静态的,无论 task 被调用多少次,只保留一份参数。所以参数的最终取值决定于最后一次调用时传入的值。这样就可以理解为什么每次结束时,打印出的 id 都是 4 了。如下图所示。

如何解决这种并发性调用,有两个思路。

一个思路是建立一个类似 Command FIFO 的机制,每个过程中对 task 的调用都往同一个 FIFO 里写,然后按 FIFO 的出口顺序执行。这样可以从逻辑上根本上解决并发的问题,回避了 Verilog 的语法解释问题。

另一个思路就是 Verilog 标准提供的 Automatic Task,在标准中是这样描述的:

All variables of an automatic task shall be replicated on each concurrent task invocation to store state specific to that invocation.

就是 Verilog 自动为 task 的每次调用分配独立的变量空间,做到互不干扰。

Automatic Task

下面采用第二种思路。代码中,只有第 32 行有变动,添加了automatic关键字。

代码语言:javascript
复制
32c32
<   task automatic test;
---
>   task test;

其运行结果为:

代码语言:javascript
复制
 100   task <1> wait ...
 100   task <1> run  ...
 200   task <2> wait ...
 300   task <3> wait ...
 400   task <4> wait ...
1100   task <1> end  ...<1>
1100   task <4> run  ...
2100   task <4> end  ...<2>
2100   task <3> run  ...
3100   task <3> end  ...<3>
3100   task <2> run  ...
4100   task <2> end  ...<4>

可以看出,task 的四次调用互不干扰了。

但是也注意到 task<1>、task<2>、task<3>、task<4> 的执行顺序和调用顺序不同。应该容易理解,这是 task 调用的栈机制导致的。把 task<1>、task<2>、task<3>、task<4> 顺序压入栈,出栈时先进后出,顺序自然相反了。这也说明,只依靠 Verilog 自身的语法,对于并发性的控制是不理想的,有时甚至是完全不符合预期的,还是需要从逻辑上来根本解决。

Static Task in Module

在Verilog标准中,还提到:

However, static tasks in different instances of a module shall have separate storage from each other.

按这个思路继续实验,先把之前的 task 放入一个 module 中,这样 test_num、test_busy 就成了该 module的instance之间独立的变量。然后在 testbench 中例化两次这个 module,并分别调用这两个module instance 中的相同 task。

代码语言:javascript
复制
module tb;

  test_m u_test_m_a();
  test_m u_test_m_b();

  initial begin
    #100;
    u_test_m_a.test(1);
  end

  initial begin
    #200;
    u_test_m_a.test(2);
  end

  initial begin
    #300;
    u_test_m_a.test(3);
  end

  initial begin
    #400;
    u_test_m_a.test(4);
  end

  initial begin
    #150;
    u_test_m_b.test(1);
  end

  initial begin
    #250;
    u_test_m_b.test(2);
  end

  initial begin
    #350;
    u_test_m_b.test(3);
  end
  
  initial begin
    #450;
    u_test_m_b.test(4);
  end

  initial begin
    #10000;
    $finish;
  end

  initial begin
    $fsdbAutoSwitchDumpfile( 500, "test.fsdb", 10 );
    $fsdbDumpvars(0, tb);
    $fsdbDumpSVA(0, tb);
  end

endmodule //tb

module test_m ();
  reg           test_busy;
  integer      test_num;

  initial begin
    test_busy = 1'b0;
    test_num  = 0;
  end

  task test;
    input [31:0]  id;
    $display($time, "\t %m task <%0d> wait ...", id);
    wait(test_busy == 1'b0);
    test_busy = 1'b1;
    $display($time, "\t %m task <%0d> run  ...", id);
    #1000;
    test_num = test_num + 1;
    $display($time, "\t %m task <%0d> end  ...<%0d>", id, test_num);
    test_busy = 1'b0;
  endtask

endmodule //test_m  

其运行结果如下:

代码语言:javascript
复制
 100   tb.u_test_m_a.test task <1> wait ...
 100   tb.u_test_m_a.test task <1> run  ...
 150   tb.u_test_m_b.test task <1> wait ...
 150   tb.u_test_m_b.test task <1> run  ...
 200   tb.u_test_m_a.test task <2> wait ...
 250   tb.u_test_m_b.test task <2> wait ...
 300   tb.u_test_m_a.test task <3> wait ...
 350   tb.u_test_m_b.test task <3> wait ...
 400   tb.u_test_m_a.test task <4> wait ...
 450   tb.u_test_m_b.test task <4> wait ...
1100   tb.u_test_m_a.test task <4> end  ...<1>
1100   tb.u_test_m_a.test task <4> run  ...
1150   tb.u_test_m_b.test task <4> end  ...<1>
1150   tb.u_test_m_b.test task <4> run  ...
2100   tb.u_test_m_a.test task <4> end  ...<2>
2100   tb.u_test_m_a.test task <4> run  ...
2150   tb.u_test_m_b.test task <4> end  ...<2>
2150   tb.u_test_m_b.test task <4> run  ...
3100   tb.u_test_m_a.test task <4> end  ...<3>
3100   tb.u_test_m_a.test task <4> run  ...
3150   tb.u_test_m_b.test task <4> end  ...<3>
3150   tb.u_test_m_b.test task <4> run  ...
4100   tb.u_test_m_a.test task <4> end  ...<4>
4150   tb.u_test_m_b.test task <4> end  ...<4>

从运行结果可以看出,test_num 在 module instance 之间独立变化。只不过因为仍是 static task,所以每个 module 的多次 task 调用存在覆盖的问题。

Automatic Task in Module

在上面例子的基础上,把 static task 改为 automatic task 。

代码语言:javascript
复制
68c68
<   task automatic test;
---
>   task test;

其运行结果如下:

代码语言:javascript
复制
 100   tb.u_test_m_a.test task <1> wait ...
 100   tb.u_test_m_a.test task <1> run  ...
 150   tb.u_test_m_b.test task <1> wait ...
 150   tb.u_test_m_b.test task <1> run  ...
 200   tb.u_test_m_a.test task <2> wait ...
 250   tb.u_test_m_b.test task <2> wait ...
 300   tb.u_test_m_a.test task <3> wait ...
 350   tb.u_test_m_b.test task <3> wait ...
 400   tb.u_test_m_a.test task <4> wait ...
 450   tb.u_test_m_b.test task <4> wait ...
1100   tb.u_test_m_a.test task <1> end  ...<1>
1100   tb.u_test_m_a.test task <4> run  ...
1150   tb.u_test_m_b.test task <1> end  ...<1>
1150   tb.u_test_m_b.test task <4> run  ...
2100   tb.u_test_m_a.test task <4> end  ...<2>
2100   tb.u_test_m_a.test task <3> run  ...
2150   tb.u_test_m_b.test task <4> end  ...<2>
2150   tb.u_test_m_b.test task <3> run  ...
3100   tb.u_test_m_a.test task <3> end  ...<3>
3100   tb.u_test_m_a.test task <2> run  ...
3150   tb.u_test_m_b.test task <3> end  ...<3>
3150   tb.u_test_m_b.test task <2> run  ...
4100   tb.u_test_m_a.test task <2> end  ...<4>
4150   tb.u_test_m_b.test task <2> end  ...<4>

符合期望。其中,进出栈顺序的原因导致执行顺序与调用顺序不符。


很有意思,对(才)吧(怪)?(!)

最后附上标准中的一段话结束本篇:

The keyword automatic declares an automatic task that is re-entrant with all the task declarations allocated dynamically for each concurrent task entry. Task item declarations can specify the following:

  • Input arguments
  • Output arguments
  • Inout arguments
  • All data types that can be declared in a procedural block
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-09-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 icsoc 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Static Task
  • Automatic Task
  • Static Task in Module
  • Automatic Task in Module
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档