>From: "Daniel Mlynek" <daniel.mlynek@aldec.com> >Back to my example I've found a LRM quotation: "Thus, disable shall end all >processes executing a particular block, whether the processes were forked by >the calling thread or not, while disable fork shall end only the processes >that were spawned by the calling thread." > >From above I assume that disable in time foo should also kill dynamic >processess spawned by fork - Am I right? No, I don't think so. The quote refers to multiple processes "executing" the block. To me that means processes that entered the block from outside. A process started inside the block is not executing the block. Would you say that a process started inside a task is executing the task? I wouldn't. The 1364 LRM was more specific for tasks: "If a task is enabled more than once, then disabling such a task shall disable all activations of the task." The quote is referring to the situation where multiple processes have entered the block and are executing it. For example: module top; task automatic foo; #20 $display($time); endtask initial foo; // process 1 initial foo; // process 2 initial foo; // process 3 initial #5 disable foo; endmodule It is saying that the disable will affect all three processes executing in the block (the task in this case, though we could add a named block inside the task and disable that instead). It is mistaken in saying that it will "end" them, since it will only cause them to exit the task. Some other activities inside the block do get ended. It is unclear whether fork..join_none processes are included in those. The quote is saying that all processes in the block are affected equally, regardless of their relationship with the process executing the disable. This is contrasted with "disable fork", which only affects processes that were created by the process executing the disable (and does end them). The quote that would be most applicable here is the one that says "All activities enabled within the named block or task shall be terminated as well." It is not clear what this means. The text makes clear that if the process has called another task, then everything further down in the call chain gets disabled. It explicitly leaves undefined the effect on nonblocking assignments and procedural continuous assignments (assign and force statements). It doesn't mention some other cases, such as $monitor, that I don't think got disabled in Verilog-XL. Since these are all free-running processes, similar to a fork..join_none subprocess, it could be argued that the same rule should apply. You can try to compare it to fork..join instead. Here I will use the behavior of Verilog-XL for comparison, since I don't know for sure that all implementations agree on this. If a process executing in a task is waiting at a join when it is disabled, all of the forked subprocesses get terminated when the task is disabled. So you could argue that subprocesses forked in the block should be terminated. But it turns out that if a task is waiting at a join when it is disabled, all of its forked subprocesses get terminated *even if they are not inside the block being disabled*. For example: module top; task bar; fork #20 $display("hello"); #30 $display("world"); join endtask task foo; bar; endtask initial foo; //initial bar; initial #5 disable foo; endmodule When foo gets disabled, so do the subprocesses created in bar, even though they were not created inside the scope of foo. So they aren't really an activity inside foo. You could try to argue that the LRM says that disabling a task within a chain of calls disables all tasks downward on the chain, so bar is being disabled, and that disables the subprocesses. But if you add a separate process that calls bar directly (like the one commented-out above), that process is not affected when foo is disabled. If bar were disabled, that would affect all processes executing in bar. So why do the fork..join subprocesses get disabled? I would argue that it is because their parent process must exit foo, but it is not allowed to continue executing until all the fork subprocesses terminate. Therefore the child processes must be terminated. The parent has temporarily divided its execution up between the subprocesses, whose execution is a replacement for its own execution. Stopping its own execution is the same as stopping its divided-up execution: i.e. the subprocesses. The child processes in a fork..join_none are not a replacement for the parent process. They run independently of the execution of the parent. The parent can exit a task whether they continue running or not. So there is no need to terminate them when the parent is disabled. It is harder to see how a fork..join_any should behave. If the parent is no longer waiting at the join_any, then the child subprocesses are as free-running as join_none subprocesses. But what if it is waiting at the join_any? Like the fork..join, it is not supposed to leave the join_any until the first child ends (as opposed to the last child ending for a join to continue). Should the disable try to satisfy that condition, as it does with a fork..join? How should it do it? Terminating one child at random would satisfy the condition, but would be arbitrary and rather silly. Also, most uses of join_any are immediately followed by a disable fork to terminate the other subprocesses. The user is using join_any and disable fork to build their own construct where the parent doesn't leave until all of the children have terminated. Leaving some children running would violate that intent. Terminating all the child processes would satisfy the condition and be fair and deterministic. It would satisfy the intent of the usual compound construct. But if the user intended to leave the children running, then it doesn't satisfy that intent. And it is prone to race conditions, where behavior depends on whether the parent woke up at the join_any before or after the disable. Leaving the child processes alone would avoid that. This is another place where it would have been better to have added a fork..join_disable construct that has the desired behavior of the compound construct. In that case it would be clear that the parent was not supposed to leave until the children were terminated. SV gives the user flexible mechanisms for building their own protocols for synchronization and control between processes. This means that the language can't assume it knows the rules the user wants for old-style disables, the way it does with fork..join. In general, the user will need to use the newer constructs to get the behavior they want. We could still define rules for old-style disables. But if users will need to use the new constructs most of the time to get the behavior they want, it may not be that important to do so. Steven Sharp sharp@cadence.com -- This message has been scanned for viruses and dangerous content by MailScanner, and is believed to be clean.Received on Wed Jul 8 13:31:08 2009
This archive was generated by hypermail 2.1.8 : Wed Jul 08 2009 - 13:32:01 PDT