Part 2 · OOP for Verification · Intermediate
extern Methods & Class File Organization
extern prototypes with out-of-body definitions via class scope ::, organizing large classes, and include-based class libraries.
Prototype inside, body outside
As classes grow, a 40-line method body buried inside the class makes the class's interface — what it offers — impossible to read at a glance. The extern qualifier solves this: declare only the prototype inside the class, and define the body later at file scope, attached to the class with the class scope resolution operator ::. The out-of-body definition is semantically inside the class — it sees this, local and protected members, everything — it is just written elsewhere.
class bus_driver;
virtual bus_if vif;
local int txn_count;
// The class body reads like a table of contents:
extern function new(virtual bus_if vif);
extern task run();
extern protected task drive_one(bus_txn t);
extern function int get_count();
endclass
// Out-of-body definitions: class_name::method_name
function bus_driver::new(virtual bus_if vif);
this.vif = vif;
endfunction
task bus_driver::run();
bus_txn t;
forever begin
t = new();
void'(t.randomize());
drive_one(t); // full access to class members
end
endtask
task bus_driver::drive_one(bus_txn t);
@(posedge vif.clk);
vif.addr <= t.addr;
txn_count++; // local member: visible, this IS the class
endtask
function int bus_driver::get_count();
return txn_count;
endfunctionMatching rules
The out-of-body signature must match the prototype exactly — return type, argument types, directions, and defaults.
Qualifiers like virtual, protected, and static appear only on the prototype, not repeated at the definition.
The definition must be in the same scope (same file/package compilation scope) as the class declaration.
Why this matters at scale
This is not cosmetic. On a real VIP, a driver class can have fifteen methods totaling a thousand lines. With extern, a new team member reads the 25-line class declaration and knows the entire API and which methods are protected extension hooks — without scrolling through implementations. It also mirrors how reviewers and interviewers think: 'show me the class' means 'show me the prototypes'. UVM library source code itself follows exactly this style.
ONE BIG CLASS BODY EXTERN-ORGANIZED
class drv; class drv;
task run(); extern task run();
... 200 lines ... extern task drive_one(...);
endtask extern function int stats();
task drive_one(); endclass ◄── API visible in 5 lines
... 150 lines ...
endtask task drv::run(); ... endtask
... task drv::drive_one(); ... endtask
endclass function int drv::stats(); ... endfunction
▲ interface buried ▲ interface first, bodies afterInclude-based class libraries
The standard packaging for a multi-class testbench library: one file per class , and a single package that \`includes them in dependency order. The package provides one compilation scope (so out-of-body definitions and forward typedef class references resolve) and one import point for users. Compile the package once; never \`include a class file directly from two different scopes, or you get two distinct copies of the 'same' class that the compiler treats as unrelated types.
// bus_pkg.sv — the ONLY file the compile list mentions
package bus_pkg;
typedef class bus_driver; // forward declaration if needed
`include "bus_txn.sv" // each file holds one class
`include "bus_driver.sv" // (with its extern bodies)
`include "bus_monitor.sv"
`include "bus_scoreboard.sv"
`include "bus_env.sv" // depends on all of the above
endpackage
// In the testbench:
import bus_pkg::*;
bus_env env = new();Key takeaways
extern splits prototype (in class) from body (at file scope, named class_name::method).
Out-of-body definitions are fully inside the class semantically — this, local, protected all work.
Prototype and definition signatures must match exactly; qualifiers stay on the prototype.
Package one class per file, included once into a single package — one compilation scope, one type.
Common pitfalls
Signature drift between prototype and out-of-body definition — compile errors that confuse beginners.
Repeating virtual/static/protected on the out-of-body definition — illegal; prototype only.
Including a class file into two scopes — two unrelated copies of the same-named class.
Include order ignoring dependencies — base classes and typedefs must precede their users.