Most programming languages have a characteristic feature called scope which defines the visibility of certain sections of code to variables and methods. The scope defines a namespace to avoid collision between different object names within the same namespace.
Verilog defines a new scope for modules, functions, tasks, named blocks and generate blocks.
module tb;
reg signal;
// Another variable cannot be declared with
// an already existing name in the same scope
reg signal;
// However, the name 'signal' can be reused inside
// a task because it belongs to a different scope.
task display();
reg signal = 1;
$display("signal = %0b", signal);
endtask
endmodule
An identifier, like a signal name, can be used to declare only one type of item in a given scope. This means that two variables of different or same data types cannot have the same name, or a task and a variable of the same name, or even a net and gate instance with the same name in the same scope.
Every identifier in Verilog has a unique hierarchical path name, where each module instance, task, function or named begin end
or fork join
block defines a new level or scope.
Hierarchical Reference Example
module tb;
// Create two instances of different modules
A uA();
B uB();
// Create a named block that declares a signal and
// prints the value at 10ns from simulation start
initial begin : TB_INITIAL
reg signal;
#10 $display("signal=%0d", signal);
end
// We'll try to access other scopes using hierarchical
// references from this initial block
initial begin
TB_INITIAL.signal = 0;
uA.display();
uB.B_INITIAL.B_INITIAL_BLOCK1.b_signal_1 = 1;
uB.B_INITIAL.B_INITIAL_BLOCK2.b_signal_2 = 0;
end
endmodule
module A;
task display();
$display("Hello, this is A");
endtask
endmodule
module B;
initial begin : B_INITIAL
#50;
begin : B_INITIAL_BLOCK1
reg b_signal_1;
#10 $display("signal_1=%0d", b_signal_1);
end
#50;
begin : B_INITIAL_BLOCK2
reg b_signal_2;
#10 $display("signal_2=%0d", b_signal_2);
end
end
endmodule
xcelium> run Hello, this is A TB signal=0 signal_1=1 signal_2=0 xmsim: *W,RNQUIE: Simulation is complete.
Upwards Name Referencing
A lower level module can reference items in a module above it in the hierarchy. For example, signal in TB_INITIAL block would be visible from the display task in A.
module A;
task display();
$display("Hello, this is A");
// Upward referencing, TB_INITIAL is visible in this module
#5 TB_INITIAL.signal = 1;
endtask
endmodule
Note that TB signal is now 1 instead of 0, because of the upward referencing change made to the signal by module A.
xcelium> run Hello, this is A TB signal=1 signal_1=1 signal_2=0 xmsim: *W,RNQUIE: Simulation is complete.
Here is another example with multiple nested modules and the leaf node can directly access members from above nodes through upward hierarchical reference.
module tb;
A a();
function display();
$display("Hello, this is TB");
endfunction
endmodule
module A;
B b();
function display();
$display("Hello, this is A");
endfunction
endmodule
module B;
C c();
function display();
$display("Hello, this is B");
endfunction
endmodule
module C;
D d();
function display();
$display("Hello, this is C");
endfunction
endmodule
module D;
initial begin
a.display(); // or A.display()
b.display(); // or B.display()
c.display(); // or C.display()
a.b.c.display();
end
endmodule
xcelium> run Hello, this is A Hello, this is B Hello, this is C Hello, this is C xmsim: *W,RNQUIE: Simulation is complete.
When compiler finds b.display() ,
- It looks in the current scope within module D to see if b is defined. If it does not exist, then it looks for the name in the enclosing scope and moves upward until the module scope is reached. If the name is still not found, it goes to the next step.
- It looks in the parent module's outermost scope and if it is not found, it keeps going up the hierarchy.