Verilog Task Concurrent Activation
旧文排版重发。
Verilog Task 的并发执行机制实验和分析。
最近做一个模块级的仿真,需要在两个过程中反复调用同一个 Task。这种场景还是比较常见的,比如一个过程作为普通的配置过程,一个作为中断服务过程,这个 Task 可以是寄存器读或写。
在某些 Case 下,发现该 Task 的防止并发执行的逻辑并不能正常工作。
于是做了些阅读和实验,弥补了一下自己在 Task 并发执行上的知识漏洞。
Static Task
先来个不符合预期的例程:
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
其运行结果为:
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
关键字。
32c32
< task automatic test;
---
> task test;
其运行结果为:
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。
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
其运行结果如下:
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 。
68c68
< task automatic test;
---
> task test;
其运行结果如下:
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
- 使用Autofac IOC组织多项目应用程序
- 国内首个“人工智能与变革管理研究院”成立
- 解决WordPress缩略图插件timthumb不支持站外图片的问题
- 让 http 2来得更猛烈些吧
- SQL Server 2008基于策略的管理
- centos系统安装Python
- rpc框架之 thrift连接池实现
- .NET Core全新路线图
- jboss eap 6.2+ 版本中 加密datasource密码等敏感信息
- WordPress自定义栏目运用实例II:添加文章Meta标签(keywords /description)
- 《Deep Learning with Python》第一章 1.1 人工智能、机器学习和深度学习
- boost::function的用法
- rpc框架之 thrift 学习 1 - 安装 及 hello world
- WordPress自定义栏目运用实例Ⅰ:添加文章来源
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 腾讯大牛教你MySQL 8.0 PFS histogram解析与优化
- Python __init__.py 作用详解
- Python创建包,导入包
- Python查看模块(变量、函数、类)方法
- Python __doc__属性:查看文档
- Python __file__属性:查看模块的源文件路径
- python第三方库的安装pip的使用与换源
- 表白必备:C语言实现会变色的心
- 人心易变,这段有趣的C代码也一样,来看一下?
- 一个简单但能考察C语言基础的题目,试试?
- 这绝对是C语言的一个经典例题了!
- 你见过最美的C语言代码是什么?
- C语言直接实现开机密码修改!
- C语言实现类似QQ聊天界面抖动功能!
- Java基础数据类型和引用类型的区别