When values need to be assigned between two different data type variables, ordinary assignment might not be valid — instead, a system task called $cast should be used. $cast can be called as either a task or a function, and it becomes especially useful in handling invalid assignments gracefully.

9 min read | Intermediate Level

What You'll Learn

  • Why ordinary assignment can fail between two different data types, and what $cast solves
  • The difference between calling $cast as a task versus as a function
  • What happens when the source value doesn't actually fit the destination type
  • How this same $cast mechanism also applies to class objects, not just enums

Syntax


function int $cast (targ_var, source_exp);

task $cast (targ_var, source_exp);

targ_var is the target variable, and source_exp is the source expression evaluated and assigned to it.

Calling as a Task vs a Function

Called asOn successOn failure
taskAssigns the valueRuntime error; target variable left unchanged
functionAssigns the value, returns 1Returns 0, no error at all; target variable left unchanged

Either way, a failed cast never leaves the target variable in some corrupted or partially-updated state — it's simply left exactly as it was before the attempt.

Using $cast

Assigning an integer value to an enumerated variable using $cast:


typedef enum { PENNY=1, FIVECENTS=5, DIME=10, QUARTER=25, DOLLAR=100 } Cents;

module tb;
	Cents 	myCent;

	initial begin
		$cast (myCent, 10 + 5 + 10);
		$display ("Money=%s", myCent.name());
	end
endmodule

Output

Output
ncsim> run
Money=QUARTER
ncsim: *W,RNQUIE: Simulation is complete.

The source expression 10 + 5 + 10 is first evaluated to 25, which matches QUARTER exactly, so the cast succeeds and myCent takes that value.

Quick Check

Q: What would $cast(myCent, 5) alone produce?

Show Answer

myCent.name() would return "FIVECENTS" — 5 matches that enum value exactly.

Without $cast

Here's what happens with a plain assignment instead:


initial begin
	myCent = 10 + 5 + 10;
	...
end

Behavior here genuinely varies by simulator, since an integer being assigned to an enumerated variable violates SystemVerilog's strong typing rules for enums. From Aldec Riviera Pro, this is a hard compile error:

Output
ERROR VCP2694 "Assignment to enum variable from expression of different type." "testbench.sv" 7  1
FAILURE "Compile failure 1 Errors 0 Warnings  Analysis time: 0[s]."

From Cadence's ncsim, it's only a warning — elaboration and simulation still proceed, and the assignment still takes effect:

Output
		myCent = 10 + 5 + 10;
		                |
ncelab: *W,ENUMERR (./testbench.sv,7|18): This assignment is a violation of SystemVerilog strong typing rules for enumeration datatypes.

ncsim> run
Money=QUARTER
ncsim: *W,RNQUIE: Simulation is complete.
ncsim> exit

Relying on this kind of assignment without $cast means the exact same code can behave very differently depending on which simulator runs it — from a hard failure to a silent (if warned-about) success.

Casting Invalid Values (as a Task)

Now trying a value that doesn't fall inside the enumerated range at all, with $cast called as a task (its return value ignored):


initial begin
	$cast (myCent, 75);
	...
end

Output

Output
ncsim> run
		$cast (myCent, 75);
		    |
ncsim: *E,INVCST (./testbench.sv,7|6): Invalid cast: the source expression can not be cast to the type of the destination variable.
Money=
ncsim: *W,RNQUIE: Simulation is complete.

The cast fails with a runtime error, and myCent is left at its default value — 0, which isn't one of the named enum values here (they start at 1), so myCent.name() prints an empty string.

Quick Check

Q: Why does Money= print with nothing after it, rather than some fallback text like "INVALID"?

Show Answer

An enum variable that never gets successfully assigned defaults to the value 0. Since none of this enum's named values happen to be 0 (they start at PENNY=1), .name() simply has no name to return for that value — it's not a special "invalid" marker, just an unnamed numeric value.

$cast as a Function

The same out-of-range attempt, this time with $cast called as a function and its return value checked:


initial begin
	if ($cast (myCent, 75))
		$display ("Cast passed");
	else
		$display ("Cast failed");
	...
end

Output

Output
ncsim> run
Cast failed
Money=
ncsim: *W,RNQUIE: Simulation is complete.

This time, the simulation completes with no runtime error at all — the failure is caught cleanly through the if/else, and myCent is left unchanged, exactly as it was in the task version.

Quick Check

Q: What's the key practical difference in program flow between the task version and the function version of this same failed cast?

Show Answer

The task version produces an unconditional runtime error message the moment the cast fails, with no way to intercept it beforehand. The function version lets the code decide exactly what to do on failure — here, printing "Cast failed" — with no error message generated at all.

Where Is $cast Used?

Casting also applies to class objects, allowing a base class handle pointing to a child object to be assigned to another handle of that specific child type — exactly the downcasting pattern covered in SystemVerilog Polymorphism. This shows up constantly in UVM, where objects are frequently passed around as generic base-class handles and need to be safely recovered as their specific derived type.

Common Beginner Mistakes

Mistake #1: Assuming task-style $cast always fails gracefully

Calling $cast as a task means a failed cast produces a runtime error, not a silent, catchable failure. Why? If there's a real chance the cast might fail, calling $cast as a function and checking its return value avoids an unconditional runtime error appearing in the log.

Mistake #2: Assuming ordinary assignment is just as safe as $cast

As the "Without $cast" example shows, a plain assignment between mismatched types can behave completely differently across simulators — a hard compile error on one, a warning-but-still-works on another. Why? $cast gives consistent, well-defined behavior regardless of which simulator runs the code, instead of relying on how strictly each tool happens to enforce enum typing rules.

Mistake #3: Assuming a failed cast sets some special "invalid" value

As both failure examples show, a failed cast simply leaves the target variable completely unchanged. Why? There's no special sentinel value assigned on failure — whatever the variable held before the attempt (often its plain default value) is exactly what it still holds afterward.

Mistake #4: Assuming $cast only works with enums

The enum examples in this article are just the simplest illustration. Why? $cast is used just as commonly with class object handles, for safely recovering a specific derived type from a more generally-typed base class handle — a pattern covered in depth in SystemVerilog Polymorphism.

Practice Exercise

Challenge: Cast a Valid and an Invalid Value

Using the same Cents enum, write code that attempts $cast as a function for two different source expressions: one that equals DOLLAR (100), and one that equals 50 (which doesn't match any enum value). Print whether each cast passed or failed.

Hint

Follow the same if ($cast(...)) ... else ... pattern from the "$cast as a Function" example, just with two separate attempts instead of one.

Solution

typedef enum { PENNY=1, FIVECENTS=5, DIME=10, QUARTER=25, DOLLAR=100 } Cents;

module tb;
  Cents myCent;

  initial begin
    if ($cast (myCent, 50 + 50))
      $display ("Cast passed: %s", myCent.name());
    else
      $display ("Cast failed");

    if ($cast (myCent, 50))
      $display ("Cast passed: %s", myCent.name());
    else
      $display ("Cast failed");
  end
endmodule

The first cast (50 + 50 = 100) succeeds, printing Cast passed: DOLLAR. The second (50) fails, since 50 doesn't match any of the enum's named values, printing Cast failed — and myCent stays at whatever DOLLAR left it at from the first successful cast, since a failed cast never changes the target.

Summary

In this tutorial, you learned:

  • $cast is used when assigning between two different data types where ordinary assignment might not be valid
  • Called as a task, a failed cast produces a runtime error; called as a function, it silently returns 0 with no error, letting the code check the result itself
  • A failed cast always leaves the target variable completely unchanged, never some special "invalid" value
  • Relying on plain assignment instead of $cast between mismatched types can produce different behavior across different simulators
  • $cast applies to class object handles too, not just enums — the same mechanism used for safe downcasting covered in SystemVerilog Polymorphism
Did you find what you were looking for?

Quiz

Test Your Knowledge

1 of 5 Score: 0 / 5