Class definitions can get very long once every method's full implementation sits directly between class and endclass, making it hard to see at a glance what a class actually does. The extern qualifier lets you declare a method's signature inside the class, and write its full implementation separately — the same scope resolution operator (::) used elsewhere ties the two together.
6 min read | Intermediate Level
What You'll Learn
- Why
externexists as a readability and organization tool, not just a syntax curiosity - How to declare several
externmethods so a class body reads like a table of contents - Why
virtualis never repeated in a method's external definition - What happens if an
externdeclaration and its external definition don't match exactly
Basic Syntax
The smallest possible example: declare inside the class, define outside it with ClassName::method_name:
class ABC;
// Let this function be declared here and defined later
// by "extern" qualifier
extern function void display();
endclass
// Outside the class body, we have the implementation of the
// function declared as "extern"
function void ABC::display();
$display ("Hello world");
endfunction
module tb;
// Lets simply create a class object and call the display method
initial begin
ABC abc = new();
abc.display();
end
endmodule
Output
ncsim> run Hello world ncsim: *W,RNQUIE: Simulation is complete.
Where extern Actually Earns Its Keep
A single one-line method doesn't show why this matters. The real value shows up once a class has several methods — the class body becomes a quick, scannable list of what the class can do, with every implementation detail moved out of the way:
class Counter;
local int value;
extern function new (int start_value = 0);
extern function void increment ();
extern function void decrement ();
extern function int get_value ();
endclass
function Counter::new (int start_value = 0);
value = start_value;
endfunction
function void Counter::increment ();
value++;
endfunction
function void Counter::decrement ();
value--;
endfunction
function int Counter::get_value ();
return value;
endfunction
module tb;
initial begin
Counter c = new (5);
c.increment ();
c.increment ();
c.decrement ();
$display ("value = %0d", c.get_value ());
end
endmodule
Reading just the class body — new, increment, decrement, get_value — tells you everything Counter can do in four lines, without needing to read a single line of any method's actual logic. Working through the calls by hand: new(5) sets value to 5, two calls to increment() bring it to 7, one decrement() brings it back to 6, so get_value() returns 6.
Even a constructor can be declared extern — extern function new(...); in the class body, and function Counter::new(...); ... endfunction outside it, exactly like any other method.
Quick Check
Q: Without reading any of the method bodies, how many distinct capabilities does the Counter class body tell you it has?
Show Answer
Four: a constructor (new), increment(), decrement(), and get_value(). That's the whole point of declaring them extern — the class body alone answers "what can this class do?" without needing to scroll through any implementation.
extern With virtual Methods and Different Return Types
extern works the same way regardless of whether a method is a task, returns a value, or is declared virtual — only the in-class declaration carries those qualifiers. The external definition never repeats them:
class ABC;
int data;
extern virtual function void display();
endclass
// Definition of an external function using scope
// resolution operator
function void ABC::display();
$display("data = 0x%0h", data);
endfunction
Notice the in-class declaration says extern virtual function void display();, but the external definition simply says function void ABC::display(); — no extern, no virtual. Both keywords only ever belong in the class body itself.
Where Is extern Used?
Base classes with many methods — the kind found throughout UVM, for instance — commonly declare most of their methods extern, keeping the class declaration itself short and readable as an overview, while each method's actual implementation can be organized separately, sometimes even split into a different file included alongside the class.
Common Beginner Mistakes
Mistake #1: Mismatching the declaration and the external definition
The argument list and return type in the extern declaration must match the external definition exactly. Why? A mismatch — a missing argument, a different default value, a different return type — is a compile error, since the compiler treats these as two halves of the same method signature that have to agree.
Mistake #2: Repeating extern or virtual in the external definition
As the display() example above shows, the external definition never repeats extern or virtual. Why? Both qualifiers describe how the method behaves from the class's perspective, and that's established once, in the class body — writing them again outside the class isn't valid syntax.
Mistake #3: Using extern for its own sake on trivial classes
extern's benefit is readability for classes with several substantial methods. Why? Splitting a one-line method like the first example into a declaration plus a separate definition adds indirection without actually making anything easier to read — it earns its keep once a class has enough methods that seeing them all listed together, without their bodies, is genuinely useful.
Practice Exercise
Challenge: Add a reset() Method
Extend the Counter class with a fifth extern method, reset(), that sets value back to 0.
Hint
Follow the exact same two-part pattern as the other methods: declare it extern inside the class, then define it separately using Counter::reset().
Solution
class Counter;
local int value;
extern function new (int start_value = 0);
extern function void increment ();
extern function void decrement ();
extern function int get_value ();
extern function void reset ();
endclass
// ... other method definitions unchanged ...
function void Counter::reset ();
value = 0;
endfunction
The class body now lists five capabilities instead of four, and nothing about the existing methods needed to change — the whole point of keeping declarations and implementations separate is that adding one doesn't disturb the others.
Summary
In this tutorial, you learned:
externdeclares a method's signature inside a class, with the full implementation written separately usingClassName::method_name- Its real value is readability: a class with several
externmethods reads like a table of contents, with implementation details moved out of the way - Even a constructor can be declared
extern, exactly like any other method virtualandexternonly ever appear in the in-class declaration — never repeated in the external definition- The declaration and definition must match exactly, or it's a compile error
Continuing the Advanced Features Chapter
Continue with SystemVerilog local vs protected.