Sunday, November 30, 2008

System Veriog GOTHCA 01

Dear Readers,

Types defined in different scopes:

The LRM words this as follows: ―The scope of a data type identifier shall include the hierarchical instance scope. In other words, each instance with a user-defined type declared inside the instance creates a unique type. To have type matching or equivalence among multiple instances of the same module, interface, or program, a class, enum, unpacked structure, or unpacked union type must be declared at a higher level in the compilation-unit scope than the declaration of the module, interface, or program, or imported from a package.

This has several implications. For example,

typedef struct {int A; int B;} AB_t;
typedef struct {int A; int B;} otherAB_t;

defines two different types and you cannot simply assign a variable of one type to a variable of the other, even though the type contents are identical. You must use an explicit type cast. GOTCHA!

Furthermore, if the type declaration of AB_t is found in module m, and m is instantiated twice, as m1 and m2, then the two types m1.AB_t and m2.AB_t are considered different types and again cannot be assigned from one to the other without an explicit cast.

However, if the typedef is found at a higher level, such as in the compilation-unit scope of the module ($unit), or in a package that is imported into the module, then the two module instances are considered to have the same type definition.

An anonymous type declaration also defines its own type. An anonymous type declaration is where the type definition appears as part of the variable declaration, and not as a separate typedef. For example:

struct {bit[15:0] value;} AB4, AB5;
struct {bit[15:0] value;} AB6;

AB4 and AB5 are defined with the same anonymous type declaration, and so they are assignment-compatible, but AB6 has a separate anonymous type definition and thus is not assignment-compatible with AB4 and AB5 without a cast, even though the type definitions are identical.

As stated in the LRM, these restrictions apply to enums, unpacked structures and unions, and classes. So they do not apply, for example, to packed structs or to arrays, packed or unpacked.

So a function can return an unpacked struct, for example, but you won‘t want to define the struct as an anonymous type in the function header, like this:

function struct {bit[15:0] value;} f(args);

because then you will not be able to assign the function return value to another variable in the calling scope, as they will be considered to have different types:

AB4 = f(args); // illegal, different types

Hope this is useful information.

Happy Reading,
ASIC With Ankit

Verilog Gotcha 02

Dear Readers,

Most of us have gotten used to the idea that numerical operands in an expression are size-extended to the size of the widest operand. We are less used to it with respect to strings, and it can hit us when we least expect it.

One particular case where it is easy to forget size-extension is in the conditional operator. If we write

cond ? expr1 : expr2

then the shorter expression of expr1 and expr2 is extended to the size of the wider one. But suppose we have something like this:

integer file; file = $fopen({"filename", dat1 ? ".dat1" : ".dat"}) ;

In this contrived example, we concatenate a file extension .dat1 or .dat to the given filename, where a variable called dat1 tells us the type of the file. If the variable dat1 is true, there is no problem, we open a file named ―filename.dat1, but if dat1 is false, then we try to open a file called ―filename .dat, with a space before ―.dat, which is extended to the size of ―.dat before being concatenated to ―filename. GOTCHA!

Actually, the shorter string literal is not extended with a space character, which is x20 ASCII, but rather with zeroes (zero-extension), which are null characters.

However, when used as a string, this often becomes a space. Note that if we had assigned the concatenation to a variable of string type, this would not occur.

string temp;
temp = {"filename", dat1 ? ".dat1" : ".dat"} ;
file = $fopen(temp) ;

The shorter string literal would still be zero-extended. However, upon assignment to string variables, null-characters are ignored, so ―.dat would still be appended directly to ―filename.