相关文章Verilog基础专栏https://blog.csdn.net/weixin_45791458/category_12263729.html1.disable语句Verilog中的disable语句提供了提前终止任务或命名块执行的能力它本质上属于过程控制语句作用类似于“强制跳出某个正在执行的过程范围”。和普通的顺序执行不同disable语句一旦生效被禁用的任务或命名块中后续尚未执行的语句将不再继续执行控制流会立即回到该任务或命名块之后的位置继续向下运行。综合工具不完全支持disable语句因为最好只在仿真时使用。图1是disable语句的BNF范式语法有关BNF范式可以参考以往文章。图1 disable语句的语法disable语句能够结束任务或命名块的执行说明如下1、在disable语句执行后受影响直接或间接任务或者命名块的执行将被停止然后直接受影响的任务使能语句和命名块之后的语句继续执行。2、在disable语句执行后受影响直接或间接的任务或命名块中已调度但还未执行的语句将不再执行。例如某个任务中原本安排了一个#5之后再执行的$display系统函数如果在这延时到来之前该任务被disable语句终止那么这条延时后的$display系统函数也就不会再执行。3、如果一个任务被并发地使能多次那么disable该任务将结束该任务的所有激活实例无论其是否是自动任务。4、在disable语句执行后受影响直接或间接的任务中从output和inout参数返回给实参的值通常在任务返回时传递是未定义的。5、在disable语句执行后受影响直接或间接的任务或命名块中的过程连续赋值assign和force的行为是未定义的。6、如果一个函数中的disable语句结束了调用直接或间接该函数的任务或命名块该行为是未定义的。2.disable语句的例子下面结合几个例子来看disable语句的具体行为。例1disable语句结束命名块的执行块后语句继续执行// 一个命名块在块内终止了自己 initial begin begin: block_name rega regb; #1 disable block_name; #2 regc rega; // 不会执行 end $display(hello world); // 在1ns时执行 end这个例子最适合用来理解disable语句对命名块的基本作用。这里block_name是一个命名块进入该块之后程序先执行rega regb;语句然后经过1ns延时执行disable block_name;语句这条语句一旦执行成功意味着名为block_name的这个块到此立即终止因此位于后面的#2 regc rega;语句不会再执行。需要特别注意的是被终止的只是这个命名块本身而不是包围它的整个initial过程所以当end退出命名块后$display(hello world);语句仍然会继续执行也就是说该例中的打印语句会在1ns时刻执行。例2disable语句结束命名块的执行间接结束命名块中正在执行的任务和任务中已调度的系统函数的执行initial begin: test task1(); //task使能 end initial begin #1 disable test; //结束命名块的执行 end task task1; #5 $display(hello world); //不会在5ns时执行 endtask这个例子进一步说明了disable语句对“间接执行内容”的影响。task1()是在命名块test中被调用的而另一个initial块在1ns时执行disable test;语句于是名为test的命名块会被终止。由于task1()正是这个命名块的一部分执行内容因此它也随之被中断任务内部原本安排在5ns时刻输出的$display(hello world);语句不会再执行。例3disable语句结束多个task的激活initial fork: test task1(); //多个task使能 task1(); //多个task使能 task1(); //多个task使能 task1(); //多个task使能 join initial begin #1 disable task1; //结束四个任务的执行 end task task1; #5 $display(hello world); //不会在5ns时执行 endtask这个例子体现了disable语句更强的一面。这里通过fork...join并发启动了四个task1()按直觉很多人可能会觉得“disable task1;语句只会结束其中某一个当前激活”但实际上并不是这样。按照标准语义disable task1;语句会终止名字为task1的所有当前激活实例所以这四个并发执行的任务都会被一起结束因此到了5ns时没有任何一个$display系统函数会真正执行。例4disable语句结束任务的返回值是未定义的module example; reg [3:0] a,b; reg [3:0] result; //定义一个task task add; input [3:0] x,y; output reg [3:0] z; begin z x y; disable add; //禁用task end endtask initial begin a 4b0011; //3 b 4b0101; //5 add(a,b,result); //调用task执行加法 $display(Result: %b,result); //输出结果是未定义的 end endmodule 不同的仿真器给出的结果也不同 对于Mentor Modelsim SE输出结果为 Result: 1000 对于Aldec Riviera Pro输出结果为 Result: 1000 对于Cadence Xcelium输出结果为 Result: 1000 对于Mentor Questa输出结果为 Result: 1000 对于Synopsys VCS输出结果为 Result: xxxx 对于Icarus Verilog输出结果为 Result: 1000这个例子讨论的是一个比较微妙但很重要的问题当任务通过output参数向外返回结果时如果任务在内部被disable结束那么这个“返回值”该怎么理解。代码里任务add先执行z x y;语句紧接着执行disable add;语句从语言直观来看好像z已经被赋值成8了因此很多人会自然觉得result肯定会打印出1000。但从标准语义角度这个任务并没有走到一个“正常完成并返回”的状态而是中途被显式终止了因此output参数回传给调用者的最终结果属于未定义行为不同仿真器可能会有不同实现。有些仿真器可能仍然把当时的z值传回去于是看到1000也有些仿真器可能更严格地把结果视为未定义于是打印出xxxx。例5一个可触发单稳态的行为描述// 事件retrig的发生将重新启动单稳定时间段并给q赋值1如果在250ns内retrig重复发生那么q将保持为1否则会变成0 always begin : monostable #250 q 0; end always retrig begin disable monostable; q 1; end这个例子是disable语句在行为建模中的一个非常典型、也非常巧妙的用法。这里前一个always begin: monostable命名块可以看成一个“延时后自动清零”的单稳态过程它每次启动后都会等待250ns再把q置0而后一个always (retrig)则表示每当retrig事件到来就先执行disable monostable;语句也就是取消当前还没走完的那段250ns延时过程然后立刻把q置1。这样一来如果retrig在250ns内再次出现那么前一次尚未结束的单稳态延时就会被中断并重新开始计时从外部看就相当于“重触发单稳态”只要触发不断重复到来q就一直保持为1直到最后一次触发后的250ns才重新回到0。
阅读完成 · 觉得有帮助?