Sometimes the compiler errors out because a class variable is used before the declaration of the class itself. This happens when two classes need a handle to each other — the classic chicken-and-egg puzzle. It occurs because the compiler processes the first class it encounters, and finds a reference to a second class that hasn't been declared yet.

9 min read | Intermediate Level

What You'll Learn

  • Why two classes referencing each other creates a "used before declared" compile error
  • How a typedef forward declaration resolves it
  • How to build a complete example where two classes actually hold and use handles to each other
  • How the same forward-declaration technique applies to parameterized classes

The Problem: A Chicken-and-Egg Reference


class ABC;
	DEF 	def; 	// Error: DEF has not been declared yet
endclass

class DEF;
	ABC 	abc;
endclass

Output

Output
file: typdef-class.sv
   DEF def;
     |
ncvlog: *E,NOIPRT (typedef-class.sv,2|5): Unrecognized declaration 'DEF' could be an unsupported keyword, a spelling mistake or missing instance port list '()' [SystemVerilog].

ABC references DEF before DEF has been declared anywhere in the file — the compiler has no way to know DEF is a class at this point.

The Fix: Forward Declaration With typedef

A forward declaration using typedef tells the compiler that a class definition will be found later in the same file:


typedef class DEF;  // Inform compiler that DEF might be
                    // used before actual class definition

class ABC;
	DEF 	def;      // Okay: Compiler knows that DEF
	                // declaration will come later
endclass

class DEF;
	ABC 	abc;
endclass

By using typedef, DEF is declared to be of type class, which is later confirmed once its actual definition appears. It isn't necessary to specify that DEF is of type class in the typedef statement:


typedef DEF; 		// Legal

class ABC;
	DEF def;
endclass

Quick Check

Q: Would typedef class DEF; still work if it were placed after class ABC instead of before it?

Show Answer

No. The compiler processes a file top to bottom, and ABC references DEF while being read — the forward declaration has to appear before the first place DEF is actually referenced, not just before DEF's own full definition.

A Complete Working Example

Here's the forward-declaration pattern actually put to use — ABC holds a handle to a DEF object and reads a value from it:


typedef class DEF;

class ABC;
  DEF def;
  int val = 10;

  function new (DEF d);
    def = d;
  endfunction

  function void show ();
    $display ("ABC.val=%0d, DEF.val=%0d", val, def.val);
  endfunction
endclass

class DEF;
  ABC abc;
  int val = 20;
endclass

module tb;
  initial begin
    DEF d = new ();
    ABC a = new (d);
    a.show ();
  end
endmodule

This prints ABC.val=10, DEF.val=20. d (a DEF object) is constructed first, then passed into ABC's constructor, which stores the handle. a.show() then reads its own val directly and def.val through the stored DEF handle — exactly the kind of cross-referencing the forward declaration exists to make possible.

Quick Check

Q: What would happen if ABC a = new(d); were changed to ABC a = new(null); instead?

Show Answer

The code would still compile fine, but a.show() would fail at run time with a null pointer dereference when it tries to read def.val — def would still be null, with no actual DEF object behind it. See SystemVerilog Class Handle for more on this exact failure mode.

Forward-Declaring a Parameterized Class

A typedef forward declaration also works for a class with a parameterized port list:


typedef XYZ;

module top;
	XYZ #(8'h3f, real)              xyz0;   // positional parameter override
	XYZ #(.ADDR(8'h60), .T(real))   xyz1;  	// named parameter override
endmodule

class XYZ #(parameter ADDR = 8'h00, type T = int);
endclass

Exactly like the non-parameterized case, XYZ is used (with parameter overrides) inside top before its actual definition appears later in the file.

Where Is This Used?

In verification environments, a driver and a monitor — or a sequencer and a scoreboard — sometimes need mutual handles to each other for tighter coordination, without one component being defined strictly "before" the other in the file. A typedef forward declaration is exactly what makes that legal, regardless of which class happens to be written first.

Common Beginner Mistakes

Mistake #1: Forgetting the forward declaration entirely

As the first example shows, referencing a class before it's declared anywhere is always a compile-time error. Why? Two mutually-referencing classes can't simply be reordered to fix this — whichever one is written first will always reference the other before it's been declared, which is exactly the case a forward declaration solves.

Mistake #2: Placing the typedef after the reference that needs it

As the first Quick Check shows, the forward declaration has to come before its first use, not just somewhere earlier in the file. Why? The compiler processes declarations in order, so a typedef placed too late doesn't help the reference that appears before it.

Mistake #3: Assuming typedef alone is a complete class definition

typedef class DEF; is only a promise that a real class definition will appear later — it isn't a substitute for one. Why? If the actual class DEF; ... endclass definition is never provided anywhere in the same compilation scope, that's still a compile error, just a different one.

Mistake #4: Assuming a null handle behaves like a valid, empty object

As the second Quick Check shows, passing null where a real object is expected compiles fine but fails at run time the moment a member is accessed through it. Why? A forward-declared type still follows all the same handle rules as any other class — it's still null until something actually constructs an object and assigns it.

Practice Exercise

Challenge: A Driver and Monitor That Reference Each Other

Using the same forward-declaration pattern, write two classes, Driver and Monitor, each holding a handle to the other. Give Driver a property tx_count and Monitor a property rx_count, and write a Monitor method that prints both counts by reading its own rx_count and the connected Driver's tx_count.

Hint

Follow the exact same structure as the ABC/DEF example — forward-declare whichever class is referenced first, then have one constructor accept and store a handle to the other.

Solution

typedef class Driver;

class Monitor;
  Driver drv;
  int rx_count = 5;

  function new (Driver d);
    drv = d;
  endfunction

  function void report ();
    $display ("rx_count=%0d, tx_count=%0d", rx_count, drv.tx_count);
  endfunction
endclass

class Driver;
  Monitor mon;
  int tx_count = 7;
endclass

module tb;
  initial begin
    Driver d = new ();
    Monitor m = new (d);
    m.report ();
  end
endmodule

This prints rx_count=5, tx_count=7 — the same cross-referencing pattern as ABC/DEF, just with names that reflect how this shows up in an actual testbench.

Summary

In this tutorial, you learned:

  • Two classes referencing each other creates a "used before declared" compile error, since the compiler processes declarations in order
  • typedef class ClassName; forward-declares a class, telling the compiler its full definition will appear later in the same file
  • The forward declaration must appear before the class's first reference, not just somewhere earlier in the file
  • The class keyword in the typedef statement is optional — typedef ClassName; works too
  • The same technique applies to parameterized classes, letting them be referenced (with parameter overrides) before their full definition appears
Did you find what you were looking for?

Quiz

Test Your Knowledge

1 of 5 Score: 0 / 5