Functions in Verilog were designed to be different from tasks for one very good reason: it was necessary for the evaluation of expressions to complete and have their value returned within a single evaluation step. In other words, the simulator must not context switch to other events until it finishes evaluating the expression and obtains the result. Functions and function calls must therefore be restricted and not allow their returning thread to block. With this restriction, blocking can only occur at a statement level, never at an expression operand level. This restriction reduces potential non-deterministic situations, and enables the simulator to optimize its runtime code. Functions have always allowed side effects (both good and bad). My definition of side effects includes the ability to modify variables outside the function boundary and calling system and PLI tasks and functions. Side effects have the potential of effectively spawning off additional threads of activity, such as @ triggers on the outside of the function boundary or PLI event creations, but this does not affect the requirement of evaluating the function in a single evaluation step. Although some side effects are considered to be bad, and certainly a source of non-determinism, some side effects are actually quite useful. For instance, PLI calls from functions can setup background activity, and this is considered a very useful (and deterministic) feature by some users. This train left the station 21 years ago! Lets not get confused with the use of functions in code for synthesis; we are talking here of code used to simulate the environment or verify the design. Now to SystemVerilog and object creation. Like the evaluation of expressions, it is highly desirable that the creation of an object be performed in a single step without blocking. Object creation involves some behind-the-scene work by the system (to manage memory for instance) in addition to calling and completing the object constructors. It is highly desirable that only the object constructor code have access (read or write) to an object under construction, that is, a partially initialized object. Otherwise, it may lead to many situations of non-determinism that the user did not intend, and that are very hard to debug. For these reasons it only makes sense to make the constructor a function not a task. Another highly desirable feature of objects is to be able to setup some background activity that does its work throughout simulation. The constructor code is the perfect place to set off this background activity, especially when true object-oriented principles are followed. The usual side effect workaround is ugly, overtly complex, and error prone, and the fork/join_none is a much better programming feature to spawn this activity. This leads to only one logical conclusion: the constructor must be a function, and it must allow fork/join_none statements. Also, because the constructor function must to be able to call other functions (simply for good programming practice), it only makes sense to allow fork/join_none in any and all functions. In general, when considering these language features, I believe we should follow two guiding principles: 1) The feature is significantly useful for users 2) The simulator can implement the feature without confusing the user Another SystemVerilog guiding principle is to avoid forcing users to code needed functionality using PLI calls, and instead enable the same functionality to be written directly in the language. The ability to have fork/join_none statements in functions satisfies these principles. PhilReceived on Sun Feb 26 09:05:57 2006
This archive was generated by hypermail 2.1.8 : Sun Feb 26 2006 - 09:06:40 PST