Namespaces

Introduction

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 Syntax

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