Hi Bob,
Thanks for taking the time to do this. Because there is no proposal
and
only arguments presented for a requirement,it is understandable that
one might have questions. I don't want you to agree to something out of
a misunderstanding. Wanting some examples
to illustrate the requirement makes sense. As long as we don't go down
a rathole of needing to see
the answer for every type or related use case in order to accept the
requirement, we are
being smart. After all, requirements serve to guide the formation of
the spec, to analyze all the
details, and evaluate afterwards whether we achieved what we wanted.
I'll try to get at your questions from the simplest of the strong types
and perhaps the one that defies the desires of users who want the
languages to do what they mean. I apologize for the length of this
email as it is partly a style problem of mine. I hope it sharpens the
argument. I also hope it addresses the comments that Michael Rohleder
made in reply to your email.
It is typical that well-known built-in types can be weakly typed and
support automatic conversions between them that make sense. It is also
typical that user defined types like enumerations, structures, and
classes are strongly typed and there are no automatic conversions
between them, except for conversions written by the creator/user of the
types. For some types, the discerning author may say there is a
possible analysis that would allow an automatic conversion and a
check(possibly runtime) to validate whether the result is legal. For
other types like unrelated class types, it is mostly obvious that it is
not possible to infer any reasonable automatic conversion. No matter.
The languages deny automatic conversion. Period.
In SV, enumerations, structures and classes are strong types. It is
generally easy to make the argument for classes that they are distinct
types and, if they are not related by inheritance, that it makes no
sense to allow assignments of one class type to an object of another
class type. Let's look at enums and see how SV makes its argument. We
can then easily see the same argument for structs and classes. An
enumeration type is an integral type that has a user-defined set of
named constants. An integral type is one of SV's basic integer data
types or packed data types that can be treated as an integer. SV
provides rules for automatically defining the encoding of enumeration
constants and also allows user-defined encoding. The values may be
disjoint.
A couple of examples are:
enum {red, yellow, green} light1, light2; // anonymous int
type
// c is automatically assigned the increment-value of 8
enum {a=3, b=7, c} alphabet;
There are richer examples of the flexibility of SV enumerations. What
does SV say about strong typing and enumerations?
6.17.3 Type checking
Enumerated types are strongly typed; thus, a variable of type enum
cannot be directly assigned a value that
lies outside the enumeration set unless an explicit cast is used or
unless the enum variable is a member of a
union. This is a powerful type-checking aid that prevents users from
accidentally assigning nonexistent values
to variables of an enumerated type. The enumeration values can still be
used as constants in expressions,
and the results can be assigned to any variable of a compatible
integral type.
...
Enumerated variables are type-checked in assignments, arguments, and
relational operators. Enumerated
variables are auto-cast into integral values, but assignment of
arbitrary expressions to an enumerated variable
requires an explicit cast.
Despite the fact that one can view an enumeration's value as an
integer, one cannot make an assignment to an object of enum data using
an integer value. It doesn't matter that it is in range or corresponds
to one of the enumeration constant's value. So,
typedef enum { red, green, blue, yellow, white, black } Colors;
Colors c;
c = green;
c = 1; // Invalid assignment
It doesn't matter that green and 1 have the same value. If I
declare a different enumeration that is similar to colors in form and
meaning(at least to me the author), I run into the same restriction,
right?
typedef enum { m_red, m_green, m_blue,
m_yellow, m_white, m_black } MyColors;
MyColors mc;
mc = m_green;
c = mc;
The point is that, while SV allows objects of an enum type to
participate in numeric expressions, it does not allow direct assignment
of an numeric value to an object of an enum type. Strong typing
enforces this kind of type checking to eliminate the possibility of
illegal values being assigned to the object. What about if I place the
same declaration of an enumeration in 2 different packages? They are
distinct types.
package p1;
typedef enum {
red, green, blue, yellow, white, black } Colors;
endpackage
package p2;
typedef enum {
red, green, blue, yellow, white, black } Colors;
endpackage
...
p1::Colors foo;
p2::Colors bar;
foo = red;
bar = foo; //Illegal
What I can do in SV, because the enumeration is an integral type and
cast compatible with other integral types, is explicitly cast an
integral expression to be of a specific enumeration type. As the
author, I must write the cast expression;there is no automatic casting
inferred. SV does understand how to do such a cast. So:
typedef enum { red, green, blue, yellow,
white, black } Colors;
Colors c;
c = green;
c = Colors'(1); // valid explicit cast
I used assignment statements and a very
simple strong type to illustrate SV strong typing rules. It applies to
arguments and actual connections to ports and parameters in SV as
well. If the types are not assignment compatible, you cannot make the
connection. What I illustrated with the enumeration data type examples
where assignments were illegal are types that are not assignment
compatible, though cast compatible.
A key point in the discussion of the requirements for strong
typing is that you should not be allowed to break this kind of strong
typing in SV because you are using mixed language and making connection
to VHDL strongly typed objects. I need to frame the rules for
VHDL enumerated types next to complete the argument.
VHDL enumerated types are strong types(as are all its types). An
enumeration type, like an integer type, is a scalar discrete type which
implies that each value of such a type has a position number that is an
integer value. It is defined by a set of identifiers or character
literals, each of which must be distinct. The position number of the
first identifier is 0 and each subsequent one has the value 1 more that
its predecessor. So, in contrast to SV, the encoding of enumeration
can only be a fixed, 0 based encoding, the same as the default encoding
in SV. Some examples are:
type BIT is ('0','1');
variable foo : BIT;
type Colors is (red, green, blue,
yellow, white, black);
variable shade : Colors;
VHDL does not allow type casting, but does support type
conversions between closely related types, but an enumeration type is
not closely related to any other type. It is possible to take an
integer value and, using an attribute of the type ( which you may
consider a pre-defined function ) convert the integer to a value of the
enumeration type. Using the variable shade from above:
shade := green;
shade := Colors'VAL(1);
I may also write a similar example as I did with SV using the same
type declaration in two different VHDL packages. The
result is the same. They are 2 distinct and unrelated types.
I'll note a couple of differences between SV and VHDL enums. First,
there are enumerations in SV for which no equivalent VHDL enumerations
can be constructed, namely those enumerations with non-default
encoding. Secondly, VHDL supports a stricter model of strong typing
for enumerations. Type casting in SV will allow you to assign a value
to an enumeration type that is illegal for the type. It is not
possible in VHDL to make that mistake. The VAL attribute will perform a
range check and report an error for a value that does not correspond to
a legal position number for the enumeration.
Strong Typing for Mixed Language
Now we can get back to the questions, using the enumerated type as
example. Both SV and VHDL expect you to declare objects of the same
enumerated type to be free to connect them across hierarchy. If I have
an SV type declared like this:
typedef enum { red, green, blue, yellow,
white, black } Colors;
It is clear to me that this is equivalent to a VHDL type declared like
this:
type Colors is (red,
green, blue, yellow, white, black);
But, if these are
separate declarations, made in each language in some declarative
region, they are also distinct, separate types. If I am cavalier about
language rules and simply say that the shape of the types is enough to
call them compatible across languages, then mixed language allows you
to violate either languages rules. I can connect an object of one type
in SV to an object of any of an arbitrary number of distinct types in
VHDL that have the same shape. I can then connect such a VHDL object
to another SV object of any of an arbitrary number of distinct types in
SV. I have used hierarchy to break SV strong typing rules. The
argument cuts both ways and I can break VHDL rules as well. This is
just wrong and there is no justification for it. Not to belabor the
point, but I don't need to pass objects through ports across a language
boundary if I have hierarchical references that cross such a boundary.
It is reasonable to state that we wish to use a single type, the "same"
type in both languages. This is a type that can be shared across SV and
VHDL. How could that be described? Well, we could define some new
canonical form for the shared type that both language would parse and
whose semantic meaning would be equivalent to the native enumerated
type, but that is a poor solution. We can say that we may write the
enumeration in either language and define the semantic rules such that
there is a clear and unambiguous equivalent type in the other
language. That is, there is a conformal map, 1 to 1, between the
types.
Now, there remain issues of where is the type declared and how does one
reference the type across the languages. Important issues, but they
are orthogonal to the discussion of this requirement. In the writeup
of the requirement, I mentioned shared packages as a way to support
shared types. Packages are a design units that will be reference-able
across languages, as a consequence of the same naming and referencing
rules we want to have for instantiations. Packages also are designed
as mechanisms to organize and share declarations. That is the purpose
they have in both languages, right? A shared package is a very
transparent, user friendly way to have shared types and preserve strong
typing. We also don't have to change either language to define such
semantics.
There are other ways. Here is one I don't like: hierarchical
references to a type across languages. This is severely restricted in
SV (for good reason) as it is and there is no mechanism in VHDL to do
this,
so we would have to change VHDL. What a high bar and long
lead time. If you think about the fundamental language design
issues...this would be a disaster.
Here's another thought. I am not fundamentally
opposed to also having some other way outside of the
language for tools to create a mapping between type definitions in SV
and VHDL that says "these types are 'the same'". I do believe it
requires a lot more consideration of what can go wrong with the
declarative approach, what kind of checking one might require to be
enforced, and what declarations will be considered erroneous.
"Erroneous" here is the precise term that means "in error, but not
required to be detectable and may lead to unpredictable or disastrous
results". I also think that any approach still has to conform to the
strong typing requirement and can't be used to circumvent it.
Applying the Argument to Other Types
It's not difficult to look at the SV struct and the VHDL record and
see how some structs and records are semantically equivalent. They must
have the same ordered set of members, each of which must be of a type
that is defined to be equivalent across the languages. The generality
we desire starts first with a definition of what built-in and
user-defined scalar types have a semantically equivalent type in the
other language. For composite user-defined types, which of those are
structurally equivalent mechanisms and what the requirements on its
members must be to arrive at a semantically equivalent type to one
defined in the other language. Where we do this, strong typing rules
can easily be preserved.
There will be types in one language which have no semantically
equivalent type in the other language. If there are compelling use
cases in which we feel we must deal with being able to connect objects
of such a type to an object in another language with some "similar"
type, let's see what they are. My belief is that most of those kind of
cases will be connections that cannot be transparently made. There
will be information loss, there will operational inconsistency, and the
ability to ruin the conceptual integrity of those objects. That should
not be standardized and does not need to be. In my view, it is the
user who must write the conversion to define how the loss of
information or synthesis/initialization of information will be done in
user-defined conversion functions.
What about user-defined classes and mixed language interoperability?
SV and SC have classes, but VHDL does not. It is conceivable that
semantic rules can be defined for some user-defined classes such that
an equivalent class in the other language may be derived, but that may
prove to be a difficult problem. If one cannot do that, the only other
possibility is for the user to write conversions or the equivalent type
themselves. The principal of protecting the semantics of strong typing
are not in conflict with this.
To be complete, there are 2 other aspects. First, let's
consider the
fact that there are a number of cases of weak types that we must deal
with. These are well-known built-in or standard types that all tools
support. We must define equivalence between these types that can be
supported automatically. A vector of wires and a std_logic vector type
both represent the same set of bits in hardware and RTL connections
between them are required. Defining how well known types across
languages relate to each other is an essential part of our
specification.
Lastly, there is the idea of an opaque or abstract type that represents
the type of object from another language that is not representable. It
can allow an object of that type to passed through design hierarchy
from language A through language B and back to language A
transparently. It requires changing language B, i.e., SV, VHDL, and
(possibly) SC to allow this. It is high bar, longer lead time item and
...so far...the only one we are entertaining.
Summary
I think what Michael acknowledged and
emphasized about Bob's concerns is the need for conversion functions in
the difficult cases. If we do a solid job of defining where semantic
equivalence exists betweens scalar types and composite types and where
it does not, we will be able to address all the type connections that
can be correctly and automatically connected. In both languages, one
can always write user-defined functions that take an object of one type
and convert to a value of another type. That takes us another solid
step. What cannot be done is to write a function in language A that
can return an object of a type that is not representable in language
A. I can't take a record in VHDL and write a VHDL function to return a
class in SV, can I? Anyone expect that we have to solve such a
problem? Note, I can do the reverse, namely take a class in SV and
write an SV function to return a struct. If that struct is a shared
type then I can connect to an object of a record type in VHDL where the
struct and record type are...the same type! Here what I have done is
kept the loss of information in the domain of the owner of the class,
SV. I can also pass a record back and within SV, use it to initialize
an object of a class type.
I hope you can see that many of the hard problems have to be owned by
the user and can be designed using the features of the existing
languages. I trust that we will come to wish for some features to be
added to other languages to make better interoperability possible and
we can advocate for them. I, for one, would like to see us get a solid
initial foundation for interoperability. We certainly don't have to
solve all the hard use cases in the first revision of the spec.
Regards, John
--
This message has been scanned for viruses and
dangerous content by
MailScanner, and is
believed to be clean.
Received on Wed Jun 27 11:42:10 2007