Deferred Shared Variables
Proposal Details
Current Situation
Defined in the analysis of VHDL2008
IR2119.
Requirement
Provides a methodology or mechanism that will permit a shared variable to be defined in the same package as the protected type.
Use Cases
See also
'Arguments For'.
Use Case: Shared Variable in Multiple Packages (Brent H)
My verification test benches often take the form as below:
-----
------- | | -------
| MDL_A |<-->| |<-->| MDL_C |
------- | | -------
| DUT |
------- | | -------
| MDL_B |<-->| | | CNTRL |
------- | | -------
-----
The models can be quite simple I/O functions to drive the DUT or quite complex BFMs.
The controller block has no physical connections to the DUT or models. It controls events via global signals and uses shared variables to set up the outputs of the models.
The shared variables are declared in packages belonging to each model, i.e. on a per model basis and requiring two packages per model.
So for the first protected type package might be:
TYPE op_type_r IS
RECORD
output1_l : Std_Logic;
output2_l : Std_Logic_Vector(2 DOWNTO 0);
output3_l : Std_Logic;
END RECORD op_type_r;
TYPE op_type_pr IS
PROTECTED
IMPURE FUNCTION value_if
RETURN op_type_r;
PROCEDURE set_p (
op_type_ri : op_type_r
);
END PROTECTED op_type_pr;
and then in the second package:
-- initialize to value
SHARED
VARIABLE op_init_rsv : op_type_pr;
This approach allows me to initialize a models outputs, alter them during simulation or set them into error conditions in order to stimulate the DUT.
My problem is two packages per model instead of one, with the extra work involved in maintenance and the obvious difficulties in code comprehension with the declarations split across two files.
Use Case: Shared Variable References in Package (Jim L)
Once declared, shared variables can also be used in a package. In the following, the shared variable RV (randomization object) is used to randomly select an initialization for the signals
GlobalInt and
GlobalSlv.
shared variable RV : RandomPType ;
signal GlobalInt : integer := RV.RandInt(0, 255);
signal GlobalSlv : std_logic_vector(1 downto 1) := RV.RandSlv(0,1,1) ;
Arguments FOR
a.
In my verification test benches, I often have shared variables defined on a model by model basis. This leads to each model requiring two packages; one for the shared variables and one for the protected types. It would simplify maintenance having one single package. My controlling block tends to be disconnected (physically) from the test bench, is driven from a TCL script and controls the simulation via shared variables and global signals. -
BAH
Test Model Control Details:
The global signals provide simulation event (time) control of the various models and the shared variables are used to control their functionality.
My global signals are typically enumerated types e.g. (init_e, run_e, pause_e, end_e) used to control the status of the model.
My shared variables are normally record types of the model's output signals, used to control their values, with corresponding matching records of boolean types setting stuck-at and/or invert status of the respective signals. These boolean records allow for error conditions to be set on the model's outputs.
These shared variable formats are, by the nature of their record structure, specific to each model, which then require a package for their protected type definitions... thus two packages per model as opposed to one. -
BAH
b.
It is something that new users would automatically think should be supported. -
BAH
Implementation Candidates
Candidate 1: Deferred Shared Variables (Brent H)
Allow a shared variable to be declared in the same package as a protected type by providing a mechanism to defer the elaboration of the
'shared variable' until after the
'protected type body' has been elaborated (in the same package).
Add a new keyword
'deferred' to indicate that the
'shared variable' elaboration needs to be deferred until the
'package body' is elaborated.
Candidate 1: Code Examples
package MY_PACKAGE is
...
type MY_PROTECTED_TYPE is
protected
...
end protected MY_PROTECTED_TYPE;
shared variable MY_VARIABLE : MY_PROTECTED_TYPE deferred;
end package MY_PACKAGE;
package body MY_PACKAGE is
...
type MY_PROTECTED_TYPE is
protected body
...
end protected body MY_PROTECTED_TYPE;
shared variable MY_VARIABLE : MY_PROTECTED_TYPE;
end package body MY_PACKAGE;
Candidate 1: Other Syntax Considerations
As one of the aims of the group is to reduce the amount of proposed new keywords, the
'deferred' keyword could possibly be replaced by the
'postponed' keyword.
(However, to me, this doesn't seem to have the right syntactical sound and I would favour sticking with the
'deferred' keyword. -
BAH)
The
'deferred' keyword could also be an additional option to the deferred
'constant' definition for uniformity.
Candidate 1: Global SIgnal Initial Values
The
'deferred' keyword could also be used to defer a (global)
'signal' initial value assignment which uses either a deferred
'shared variable' access method, a deferred
'constant' value, or a
'function' return value.
Candidate 1: Arguments AGAINST
This seems to be a significant change to VHDL's elaboration model.
Candidate 1: Language Concerns (CliffordWalinsky)
- I don't understand the utility of the new deferred (or even postponed) keyword. If the keyword is missing, that presumably means a shared variable of the protected type cannot be defined in the package header. Certainly, a compiler can check for the presence of a declared shared variable and the presence of the deferred keyword in the type definition. Except, I don't understand why one wouldn't want to always use the deferred keyword, once issues concerning elaboration of a shared variable within the body of the package have been addressed.
<Brent Hayhoe> For a constant, the omission of the expression assignment is normally defined as an error condition, except when used in the declarative part of a package. The omission here implies the constant as deferred and it is then assigned during elaboration of the package body. The main advantage of this is the ability to use functions, defined in the same package, within the expression assignment of the constant.
If we were to declare a global signal in a package and assign it an initial value using a function within the expression, without the 'deferred' keyword there is no way for the compiler to imply its deferred status. The omission of its initial value expression is quite valid and cannot be used to imply a deferred status.
For a shared variable, although we could imply a deferred status by the fact that its protected type is defined in the same package, it would seem to put more effort onto the compiler function rather than require the new 'deferred' keyword to explicitly define its deferred status.
</Brent Hayhoe>
- The language standard should be expanded to explicitly state that elaboration of a protected type body shall occur during elaboration of the package body that contains it. Further, it needs to be explicitly stated that a design is erroneous if elaboration of a protected type body refers to a deferred constant or other shared variable that has not yet been elaborated.
Candidate 2: Allow Protected Type Bodies in Package Declaration Region (Jim L)
Allow a protected type body (and perhaps a subprogram body) to be included in a package declarative area. As a result, a shared variable could then be declared immediately after the protected type body - in an identical fashion to its use in an architecture declaration region. This use model would also make it similar to how other languages use classes.
The protected type body then would be the syntax that separates the public space of the protected type from the private space.
Arguments Against
Needs use cases not "Arguments For", so really cannot judge how important this is.
Email Reflector Comments
From: JimLewis (Mon Dec 17 2012 - 14:33:23 PST)
A use case that illustrates what your are trying to do and why would be helpful.
I am using protected types quite extensively, but I suspect that I am using them for a different purpose than you. Most of my shared variable declarations I use are private to an instance of a design and I declare them in the architecture declaration region.
Are you putting the shared variable declarations in a package to allow different instances (perhaps of the same design) to share a data structure?
Currently I am more limited by not allowing shared variables on interfaces and not allowing arrays of shared variables. I also need generics on protected types so I can effect an initializer.
From: MartinThompson (Tue Dec 18 2012 - 01:32:30 PST)
I read your
DeferredSharedVariables page yesterday, wasn't aware of the history, so thanks for pointing me to that.
As with Jim, my use of shared variables is limited to "local" instances, so I haven't come across the problem - I would also appreciate an example of when you find this useful.
My immediate thought on reading your description was that, rather than introduce a 'deferment', can we not change things so that the "expected" behaviour actually happens? Is there a good reason for not being able to declare a shared variable in the same region as its type definition?
From: JonathanBromley (Tue Dec 18 2012 - 01:48:39 PST)
Brief comment from the sidelines by an interested but inactive observer:
I don't have as much experience in using protected types as I do with other kinds of OO-style programming, so I may be off track here, but... If a package provides some protected type as a service to the rest of your code, there may well be situations where the same package also should provide some "specimen" instances of that type - maybe as templates that help in the construction of new instances, maybe as predefined globally accessible objects (I could imagine that being useful, for example, for an objections mechanism).
It does seem perverse that you can't actually create one of the shared variables inside the package that defines their type. For example, in
SystemVerilog I would find it very troublesome if I could not declare a package-level variable of a class type in the same package that defines the class.
From: JimLewis (Tue Dec 18 2012 - 09:25:29 PST)
One thing that differentiates VHDL from other languages is the division between protected type declaration and body. In addition within a package, the protected type body must be in the package body. Hence, in the package declarative region the protected type is not yet fully defined.
In the current language, I can make the following shared variable declaration and then use it. Should I be able to do this with "deferred" shared variables or should it be illegal? This would mean then that the elaboration of anything that references the shared variable also needs to be deferred.
shared
variable RV : RandomPType ;
signal GlobalInt : integer := RV.RandInt(0, 255);
signal GlobalSlv : std_logic_vector(1 downto 1) := RV.RandSlv(0,1,1) ;
The reason we need to see use models is that there may be less complicated solutions that acceptably solve the problem. What is the real problem? Having to put the shared variable in a different package or having more than one package reference.
One alternate solution could to allow a protected type body (and perhaps a subprogram body) to be included in a package declarative area. This then would make a shared variable use model identical to an architecture.
If the problem is having more than one package reference, then don't forget about the VHDL-2008 context clause.
From: Brent Hayhoe (Tue Dec 18 2012 - 09:25:29 PST)
I have put a few lines on the proposal page trying to explain how I use shared variables and the problems I have.
Looking at your example (below)above at first confused me, until I realised what you are doing (I think).
I presume that you are using the method sub-programs from the protected type to set the variable and use it to assign the initial values to the global signals.
I assume that if you had used functions and constants to achieve this, then the constants would have had to be deferred?
However, your point is well made and perhaps this shows that we may need to allow (global) signals to be deferrable as well? This would sync well into the language I think.
As to you suggestion whether we allow protected type bodies into the package declarative part, I do not know, but my gut instinct is probably no. However, I have to defer (pun?) to the language and compiler experts amongst us on this point.
The example from Jim L is clearly invalid:
RandInt cannot be called before being elaborated. One way to fix that (as suggested by Jim L) would be to allow protected bodies in package specification. That breaks a fundamental software engineering rule (spec vs impl), but why not.
I don't really like the addition of
deferred reserved word. Furthermore, deferred declarations can create a lot of elaboration issue. What about allowing statements in a package body:
package body pkg is
...
begin
-- Statements executed during elaboration of the package body.
my_var := xxx;
end pkg;
That doesn't work with signal but I think this is not an important restriction.
Supporters
--
Brent Hayhoe - 2012-12-16
--
PatrickLehmann - 2016-07-18
Analysis
This proposal was discussed in the
2016 May 26th meeting under item 81 of the ranked proposals spreadsheet for VHDL-201x.
The requirement has similarities to deferred constants.
At least one HDL simulator vendor supports this (currently) non-standard functionality with a tool compiler switch, i.e. to solve the problem of a declaration of both a
shared variable with its
protected type within the same package.
The meeting decided that it would be better syntactically if the new reserved word
deferred appeared at the beginning of the statement, becoming more like an object class modifier.
The new reserved word
deferred can now be used as an alternative to omitting the value assignment in a
constant declaration, in order to allow the same delayed value assignment functionality for its initial value expression.
The new reserved word
deferred can also be used in a
signal declaration in order to allow the same delayed value assignment functionality for its initial value expression as that of a
deferred constant.
The new reserved word
deferred can also be used in a 'normal'
variable declaration (when associated with a non-protected type) in order to allow the same delayed value assignment functionality for its initial value expression as that of a
deferred constant. A
variable, when associated with a protected type, can also be deferred in order to allow its analysis to be delayed similarly to that of a
deferred shared variable.
Italic prefixes in BNF productions are used to impart extra semantic information where a complete definition of the BNF production is either not possible or too complex to construct. This 'extra' information is detailed within the associated LRM text. The 'subtype indication' BNF symbols used within the BNF expressions in various BNF productions already include such italic prefixes and the existing BNF symbol occurrences are further expanded in order to enhance semantic information.
In the
2016 November 24th meeting it was decided to split this LCS into two (
LCS-2016-080a &
LCS-2016-080b) in order to separate out this last point from the main proposal for the sake of clarity.
Package Declarations
A
package may be declared in a number of different places within the VHDL code structure, along with its
package body (if required). The most common of these is as a separate design unit and its declared items referenced via a
use clause.
Because this kind of
package can be referenced by concurrent code, it must not declare a normal
variable within its package declaration. However a
shared variable can be declared here. Similarly, a
signal may also be declared here and is often referred to as a global
signal, because of the ability for it to be made visible anywhere by a
use clause.
A
package may also be declared in a number of other code structure declarative regions.
When a
package is declared within the declarative regions of an
entity, an
architecture, or a
block, the same concurrent code restrictions apply as those of a primary design unit specified above.
A
package may also be declared within the declarative regions of a
process, a subprogram, or a
protected body of a
protected type and, because these structures define sequential code, the reverse declaration rules apply. In these areas a normal
variable may be declared, but neither a
shared variable nor a
signal declaration is allowed.
A
package may also be declared within a
package declaration or a
package body. In these cases, the declarative rules for a
variable,
shared variable and
signal are defined by those of its parent
package declaration.
The
deferred versions of these object classes follow the same declarative rules, but may only be declared within a
package declaration and its full declaration must appear within the associated
package body.
Deferred Constants
If we wish to define a
function plus a
constant in the same
package and in so doing we want to use the
function to define the value of the
constant then the value cannot be assigned in the
package declaration because at this point in the
analysis phase
it knows nothing about the body of the
function.
package my_package is
function my_function(a, b : integer) return my_signed_type;
constant my_constant : my_signed_type; -- deferred constant
end package my_package;
If we just move the
constant declaration to the
package body (after the
function body) the problem is resolved. However, the
constant cannot now be made visible (by a
use statement) outside the scope of the
package.
In order to overcome this problem, the value assignment of the
constant in the
package declaration is omitted and this defines the object as a
deferred constant giving visibility to the
constant assigned in the
package body.
package body my_package is
function my_function(a, b : integer) return my_signed_type is
begin
...
end function my_function;
constant my_constant : my_signed_type := my_function(1, 2); -- constant value
end package my_package; -- assigned
The
constant declaration is repeated in the
package body (after the
function body) with the value assignment included.
Deferred Shared Variables
If we wish to define a
protected type plus a
shared variable in the same
package there is a similar problem.
The
shared variable cannot be declared in the
package declaration because at this point in the
analysis phase
it knows nothing about the body of the
protected type and its methods and local variable(s), only the declaration of the methods.
package my_package is
type my_protected_type is protected
function get return my_signed_type;
procedure set(value : my_signed_type);
end protected my_protected_type;
deferred shared variable my_shared_variable : my_protected_type; -- deferred shared
-- variable
end package my_package;
If we just move the
shared variable declaration to the
package body (after the
protected type body) the problem is resolved. However, the
shared variable cannot now be made visible (by a
use statement) outside the scope of the
package.
The work-around is to declare the
shared variable in a second
package which 'uses' this
package.
package body my_package is
type my_protected_type is protected body
variable local : my_signed_type;
function get return my_signed_type is
begin
;
end function get;
procedure set(value : my_signed_type);
begin
;
end procedure set;
end protected my_protected_type;
shared variable my_shared_variable : my_protected_type; -- analysed shared
-- variable
end package my_package;
A better solution to overcome this problem is to delay the analysis of the
shared variable to the body of the
package, but unlike
deferred constant a new keyword -
deferred - is added after the subtype indication giving visibility to the
shared variable declared in the
package body.
Deferred Non-Shared Variables
A normal non-shared variable can also be associated with a
protected type, giving two forms of a
deferred variable; first, allowing a function call in the initialization expression (similar to a
deferred constant); second, allowing a
variable of a
protected type to be declared in the same package as its
protected type declaration and body (similar to a
deferred shared variable).
package my_package is
function my_function(a, b : integer) return my_signed_type;
type my_protected_type is protected
function get return my_signed_type;
procedure set(value : my_signed_type);
end protected my_protected_type;
deferred variable my_variable1 : my_signed_type; -- deferred variable
deferred variable my_variable2 : my_protected_type; -- deferred variable
end package my_package;
Both variable's 'deferred' declarations appear in the
package body.
package body my_package is
function my_function(a, b : integer) return my_signed_type is
begin
...
end function my_function;
type my_protected_type is protected body
variable local : my_signed_type;
function get return my_signed_type is
begin
;
end function get;
procedure set(value : my_signed_type);
begin
;
end procedure set;
end protected my_protected_type;
variable my_variable1 : my_signed_type := my_function(1, 2); -- variable initial
-- value defined
variable my_variable2 : my_protected_type; -- analysed variable
end package my_package;
Deferred Signals
If we wish to define a
function plus a global
signal in the same
package and in so doing we want to use the
function to initialize the value of the global
signal then this now becomes possible.
package my_package is
function my_function(a, b : integer) return my_signed_type;
deferred signal my_global_signal : my_signed_type; -- deferred global
-- signal
end package my_package;
We can now delay the analysis of the global
signal to the body of the
package using the new keyword -
deferred.
package body my_package is
function my_function(a, b : integer) return my_signed_type is
begin
...
end function my_function;
signal my_global_signal : my_signed_type := my_function(1, 2); -- global signal
end package my_package; -- initial value
-- defined
BNF Productions
Object Class Productions
constant_declaration ::=
normal_constant_declaration
| deferred_constant_declaration
normal_constant_declaration ::=
constant identifier_list : constant_subtype_indication := expression ;
deferred_constant_declaration ::=
[ deferred ] constant identifier_list : constant_subtype_indication ;
variable_declaration ::=
local_variable_declaration
| shared_variable_declaration
local_variable_declaration ::=
normal_variable_declaration
| deferred_variable_declaration
normal_variable_declaration ::=
variable identifier_list : nonprotected_subtype_indication [ := expression ] ;
| variable identifier_list : protected_subtype_indication ;
deferred_variable_declaration ::=
deferred variable identifier_list : subtype_indication ;
shared_variable_declaration ::=
normal_shared_variable_declaration
| deferred_shared_variable_declaration
normal_shared_variable_declaration ::=
shared variable identifier_list : protected_subtype_indication ;
deferred_shared_variable_declaration ::=
deferred shared variable identifier_list : protected_subtype_indication ;
signal_declaration ::=
normal_signal_declaration
| deferred_signal_declaration
normal_signal_declaration ::=
signal identifier_list : signal_subtype_indication [ signal_kind ] [ := expression ] ;
deferred_signal_declaration ::=
deferred signal identifier_list : signal_subtype_indication [ signal_kind ] ;
file_declaration ::=
file identifier_list : file_subtype_indication [ file_open_information ] ;
Interface Object Class Productions
interface_constant_declaration ::=
[ constant ] identifier_list : [ in ] constant_subtype_indication [ := static_expression ]
interface_variable_declaration ::=
[ variable ] identifier_list : [ mode ] nonprotected_subtype_indication [ := static_expression ]
| [ variable ] identifier_list : inout protected_subtype_indication
interface_signal_declaration ::=
[ signal ] identifier_list : [ mode ] signal_subtype_indication [ bus ] [ := static_expression ]
interface_file_declaration ::=
file identifier_list : file_subtype_indication
Package Declarative Items
package_declarative_part ::=
{ general_package_declarative_part }
| { concurrent_package_declarative_part }
| { sequential_package_declarative_part }
general_package_declarative_part ::=
general_package_declarative_item
concurrent_package_declarative_part ::=
general_package_declarative_item
| concurrent_package_declarative_item
sequential_package_declarative_part ::=
general_package_declarative_item
| sequential_package_declarative_item
general_package_declarative_item ::=
subprogram_declaration
| subprogram_instantiation_declaration
| package_declaration
| package_instantiation_declaration
| nonprotected_type_declaration
| subtype_declaration
| protected_type_declaration
| normal_constant_declaration
| deferred_constant_declaration
| file_declaration
| alias_declaration
| component_declaration
| attribute_declaration
| attribute_specification
| use_clause
| group_template_declaration
| group_declaration
concurrent_package_declarative_item ::=
normal_signal_declaration
| deferred_signal_declaration
| normal_shared_variable_declaration
| deferred_shared_variable_declaration
| disconnection_specification
| PSL_Property_Declaration
| PSL_Sequence_Declaration
sequential_package_declarative_item ::=
normal_variable_declaration
| deferred_variable_declaration
Package Body Declarative Items
package_body_declarative_part ::=
{ general_package_body_declarative_part }
| { concurrent_package_body_declarative_part }
| { sequential_package_body_declarative_part }
general_package_body_declarative_part ::=
general_package_body_declarative_item
concurrent_package_body_declarative_part ::=
general_package_body_declarative_item
| concurrent_package_body_declarative_item
sequential_package_body_declarative_part ::=
general_package_body_declarative_item
| sequential_package_body_declarative_item
general_package_body_declarative_item ::=
subprogram_declaration
| subprogram_body
| subprogram_instantiation_declaration
| package_declaration
| package_body
| package_instantiation_declaration
| nonprotected_type_declaration
| subtype_declaration
| protected_type_declaration
| protected_type_body
| normal_constant_declaration
| file_declaration
| alias_declaration
| attribute_declaration
| attribute_specification
| use_clause
| group_template_declaration
| group_declaration
concurrent_package_body_declarative_item ::=
normal_signal_declaration
| normal_shared_variable_declaration
sequential_package_body_declarative_item ::=
normal_variable_declaration
Other Structures
Design Unit Declarative Items
library_unit ::=
primary_unit
| secondary_unit
primary_unit ::=
entity_declaration
| configuration_declaration
| nonsequential_package_declaration
| nonsequential_package_instantiation_declaration
| context_declaration
| PSL_Verification_Unit
secondary_unit ::=
architecture_body
| nonsequential_package_body
Entity Declarative Items
entity_declarative_part ::=
{ entity_declarative_item }
entity_declarative_item ::=
subprogram_declaration
| subprogram_body
| subprogram_instantiation_declaration
| nonsequential_package_declaration
| nonsequential_package_body
| nonsequential_package_instantiation_declaration
| type_declaration
| subtype_declaration
| normal_constant_declaration
| normal_signal_declaration
| normal_shared_variable_declaration
| file_declaration
| alias_declaration
| attribute_declaration
| attribute_specification
| disconnection_specification
| use_clause
| group_template_declaration
| group_declaration
| PSL_Property_Declaration
| PSL_Sequence_Declaration
| PSL_Clock_Declaration
Architecture and Block Declarative Items
architecture_declarative_part ::=
{ block_declarative_item }
block_declarative_part ::=
{ block_declarative_item }
block_declarative_item ::=
subprogram_declaration
| subprogram_body
| subprogram_instantiation_declaration
| nonsequential_package_declaration
| nonsequential_package_body
| nonsequential_package_instantiation_declaration
| type_declaration
| subtype_declaration
| normal_constant_declaration
| normal_signal_declaration
| normal_shared_variable_declaration
| file_declaration
| alias_declaration
| component_declaration
| attribute_declaration
| attribute_specification
| configuration_specification
| disconnection_specification
| use_clause
| group_template_declaration
| group_declaration
| PSL_Property_Declaration
| PSL_Sequence_Declaration
| PSL_Clock_Declaration
Subprogram Declarative Items
subprogram_declarative_part ::=
{ subprogram_declarative_item }
subprogram_declarative_item ::=
subprogram_declaration
| subprogram_body
| subprogram_instantiation_declaration
| nonconcurrent_package_declaration
| nonconcurrent_package_body
| nonconcurrent_package_instantiation_declaration
| type_declaration
| subtype_declaration
| normal_constant_declaration
| normal_variable_declaration
| file_declaration
| alias_declaration
| attribute_declaration
| attribute_specification
| use_clause
| group_template_declaration
| group_declaration
Protected Type Body Declarative Items
protected_type_body_declarative_part ::=
{ protected_type_body_declarative_item }
protected_type_body_declarative_item ::=
subprogram_declaration
| subprogram_body
| subprogram_instantiation_declaration
| nonconcurrent_package_declaration
| nonconcurrent_package_body
| nonconcurrent_package_instantiation_declaration
| type_declaration
| subtype_declaration
| normal_constant_declaration
| normal_variable_declaration
| file_declaration
| alias_declaration
| attribute_declaration
| attribute_specification
| use_clause
| group_template_declaration
| group_declaration
Process Declarative Items
process_declarative_part ::=
{ process_declarative_item }
process_declarative_item ::=
subprogram_declaration
| subprogram_body
| subprogram_instantiation_declaration
| nonconcurrent_package_declaration
| nonconcurrent_package_body
| nonconcurrent_package_instantiation_declaration
| type_declaration
| subtype_declaration
| normal_constant_declaration
| normal_variable_declaration
| file_declaration
| alias_declaration
| attribute_declaration
| attribute_specification
| use_clause
| group_template_declaration
| group_declaration