SystemVerilog Namespaces provide a mechanism for sharing data, type, task, and function declarations amongst multiple SystemVerilog modules and interfaces. A Namespace is a formalism and limitation to SystemVerilog 3.0 and 3.1 $root declarations.
Namespaces are explicitly named scopes appearing at the outer level of
the source text (at the same level as modules, primitives, interfaces, etc.).
Types, variables, tasks, and functions may be declared within a namespace.
Such declarations may be referenced within modules, macromodules, interfaces,
and other namespaces by either import or hierarchical name.
namespace_declaration ::= { attribute_instance } namespace namespace_identifier { namespace_item } endnamespace [ : namespace_identifier ] namespace_item ::= net_declaration | data_declaration | task_declaration | function_declaration | dpi_import_export | namespace_import_declaration | class_declaration |
The namespace declaration creates a top-level region to contain declarations
intended to be shared amongst one or more modules, macromodules, or interfaces.
Items within namespaces are generally type definitions, tasks, and functions.
It is also possible to populate namespaces with variables and nets. This
may occasionally be useful for globals that aren't conveniently passed down
through the hierarchy.
The following is an example of a namespace:
namespace ComplexPkg;
typedef struct {
float i, r;
} Complex;
function Complex add(input Complex a, b)
add.r = a.r + b.r;
add.i = a.i + b.i;
endfunction
function Complex mul(input Complex a, b)
mul.r = (a.r * b.r) + (a.i * b.i);
mul.i = (a.r * b.i) + (a.i * b.r);
endfunction
endnamespace : ComplexPkg
Certainly, one of the ways to utilize declarations made in namespaces,
is to reference them hierarchically:
ComplexPkg.Complex cout = ComplexPkg.mul(a, b);
Import Syntax
namespace_import_declaration ::= { attribute_instance } import namespace_import_item { , namespace_import_item } ; namespace_import_item ::= namespace_identifier . identifier | namespace_identifier . * |
The import statement provides direct visibility of symbols within namespaces.
It allows those symbols declared within namespaces to be visible within the
current scope by its declared simple name. Two forms of the import statement
are provided. The first form is an explicit import and allows control over
precisely which symbols are imported:
import ComplexPkg.Complex;
import ComplexPkg.add;
The second form of import is a wildcard import and allows all symbols
defined within a namespace to be imported as a group:
import ComplexPkg.*;
Explicit imports are treated similarly to a declaration.
An explicit import is illegal if another symbol by the same name has already
been declared or imported into the same scope. Similarly, after importing
a symbol by a given name, it's illegal to then declare a symbol by that
same name within the same scope.
Wildcard imports have somewhat different restrictions. All the symbols
within a namespace implied by a wildcard import are candidates for import.
They in fact become imported only if there are no other symbols by the same
name declared or imported in the same scope. Similarly, their visibility
may be limited by a subsequent declaration of the same name in the same scope.
It is also possible for two symbols in two wildcard imports to "hide" each
other.
Import statements usually affect the scope in which they appear. The exception
to this policy is when one or more import statements appears immediately
prior to a module or interface declaration. In these cases, the import statement(s)
affect the subsequent module or interface scope. Thus:
import ComplexPkg.*;
module cadder(input Complex a, b, output Complex z);
assign z = add(a, b);
endmodule