To manage the mutability of class properties, SystemVerilog provides the const keyword, allowing properties to be made read-only. Due to the dynamic nature of class objects, this manifests in two distinct forms: global constant class properties and instance constant class properties.
9 min read | Intermediate Level
What You'll Learn
- The difference between a global constant class property and an instance constant class property
- Why global constants are commonly declared
static, and why instance constants never can be - Why an instance constant can only ever be assigned inside the constructor
- What happens if you try to assign either kind of constant from outside its class
Global Constant Class Properties
A global constant class property is a read-only data member assigned its value as part of its own declaration. Like other const variables in SystemVerilog, its value can't be changed anywhere else after that initial declaration. A common practice is to declare global constants static as well, meaning every object of the class shares the exact same constant value:
class Jumbo_Packet;
// max_size is a global constant, same for all Jumbo_Packet instances
const int max_size = 5 * 10;
byte payload [];
function new(int size);
// payload size is limited by the global constant max_size
payload = new[ size > max_size ? max_size : size ];
endfunction
endclass
module tb;
Jumbo_Packet m_pkt;
initial begin
m_pkt = new(100);
$display("size=%d", m_pkt.payload.size());
end
endmodule
Output
xcelium> run size= 50 xmsim: *W,RNQUIE: Simulation is complete.
max_size evaluates to 50 at declaration time. Since the requested size (100) exceeds it, the constructor clamps payload's actual size down to max_size instead.
Quick Check
Q: If a second object were created with new(30) instead, what would its payload size be?
Show Answer
30. Since 30 doesn't exceed max_size (50), the ternary in the constructor takes the requested size as-is, with no clamping needed.
Instance Constant Class Properties
An instance constant class property doesn't include an initial value in its declaration — only the const qualifier. It can be assigned a value exactly once at run time, and that assignment must happen inside the class's own constructor. Unlike global constants, instance constants can never be declared static, since that would prevent them from being assigned per-object inside the constructor at all — their entire purpose is to differ from one object to the next:
class Big_Packet;
// size is an instance constant, determined at object creation
const int size;
byte payload [];
function new();
// The instance constant 'size' is assigned its value once in the constructor
size = $urandom % 10;
payload = new[ size ];
endfunction
endclass
module tb;
Big_Packet m_pkt;
initial begin
m_pkt = new();
$display("size=%d", m_pkt.payload.size());
// This will result in a compilation error
// xmvlog: *E,CLSCON (testbench.sv,23|13): Instance constant class properties cannot be assigned outside of a constructor.
// m_pkt.size = 50;
end
endmodule
Output
xcelium> run size= 4 xmsim: *W,RNQUIE: Simulation is complete.
size is assigned once, inside new(), and its exact value here depends on the simulation's random seed — a different seed would produce a different (but still fixed, once assigned) value for that specific object.
Here's that commented-out line demonstrated directly, as its own standalone example:
class Big_Packet;
const int size;
byte payload [];
function new();
size = $urandom % 10;
payload = new[ size ];
endfunction
endclass
module tb;
Big_Packet m_pkt;
initial begin
m_pkt = new();
m_pkt.size = 50; // Attempting to reassign the instance constant from outside its constructor
end
endmodule
Output
xmvlog: *E,CLSCON (testbench.sv,23|13): Instance constant class properties cannot be assigned outside of a constructor.
Quick Check
Q: Does m_pkt.size = 50; fail at compile time or run time?
Show Answer
Compile time — note the ncvlog/xmvlog prefix on the error, which is the compiler's own analysis stage, not the simulator actually running anything. The restriction is checked before simulation ever starts.
Difference Between Global and Instance
| Global constant | Instance constant | |
|---|---|---|
| Initial value | Given at declaration | None at declaration |
| When assigned | Once, at declaration | Once, inside the constructor |
| Can be static | Yes | No |
| Value across objects | Same for every instance | Can differ per instance |
Where Are Const Class Properties Used?
A global constant fits a protocol-wide fixed limit that's genuinely the same for every object — a maximum burst length, or a maximum payload size shared across every transaction. An instance constant fits a value that's fixed once an object is created but is meant to vary between objects — like a uniquely assigned (and then immutable) transaction ID.
Common Beginner Mistakes
Mistake #1: Trying to assign an instance constant outside its constructor
As the standalone CLSCON example shows, this is always a compile-time error, no matter where outside the constructor the attempt happens. Why? An instance constant's entire point is to be fixed exactly once, at construction — allowing any later assignment would defeat that guarantee entirely.
Mistake #2: Declaring an instance constant as static
This isn't allowed. Why? static means one shared value across every object, but an instance constant exists specifically to let each object have its own distinct value, assigned individually inside its own constructor call — the two ideas are directly incompatible.
Mistake #3: Confusing global and instance constants' assignment timing
A global constant's value is fixed once for the entire class, computed at declaration — it can't be recalculated per object the way an instance constant can. Why? If a value genuinely needs to vary between objects (like a random per-object ID), it has to be an instance constant, assigned in the constructor — a global constant simply can't do that.
Mistake #4: Assuming a const property without static means "per instance" automatically
A non-static const property still needs to actually declare an initial value or be assigned in the constructor to be either a global or instance constant, respectively — the static keyword itself isn't what determines which kind it is. Why? The real distinguishing factor is whether the value is given at declaration (global) or left for the constructor to assign (instance), not the presence or absence of static alone — though static is only ever legal on the global kind.
Practice Exercise
Challenge: Combine Both Kinds of Constant
Write a class Transaction with a global constant static const int MAX_ID = 100; and an instance constant const int id;. In the constructor, accept a requested_id argument, and assign id to whichever is smaller: requested_id or MAX_ID. Create two objects — one with a requested ID under the limit, one over it — and display both.
Hint
The clamping logic here is the same ternary pattern used in the Jumbo_Packet example — just applied to an instance constant instead of a plain property.
Solution
class Transaction;
static const int MAX_ID = 100;
const int id;
function new (int requested_id);
id = (requested_id > MAX_ID) ? MAX_ID : requested_id;
endfunction
endclass
module tb;
initial begin
Transaction t1 = new (50);
Transaction t2 = new (150);
$display ("t1.id=%0d, t2.id=%0d", t1.id, t2.id);
end
endmodule
This prints t1.id=50, t2.id=100. t1's requested ID (50) is under the shared limit, so it's used as-is; t2's requested ID (150) exceeds MAX_ID, so it's clamped down to 100 — each object's id is fixed permanently the moment its constructor finishes.
Summary
In this tutorial, you learned:
- A global constant class property is assigned its value at declaration, and typically declared
staticso every object shares it - An instance constant class property has no initial value at declaration, and is assigned exactly once, inside the constructor — letting each object have its own distinct value
- An instance constant can never be declared
static, since that would conflict with assigning it per-object - Assigning either kind of constant outside its allowed point — declaration for global, constructor for instance — is a compile-time error, not a run-time one