Every now and then you need to avoid recompiling a testbench just to change one setting — accepting values from the command line the way a scripting language like bash or Perl would. SystemVerilog does this through plusargs: optional arguments always starting with a + character, passed to the simulation at run time and read back inside the testbench through two system functions.
9 min read | Intermediate Level
What You'll Learn
- The difference between
$test$plusargs(presence check) and$value$plusargs(value extraction) - Exactly how
$test$plusargs's prefix matching works, and why it's case-sensitive - How
$value$plusargs's format specifier controls how the plusarg's text is reinterpreted, not just how it's displayed - Common command-line syntax pitfalls: a missing
=, an empty value, and a doubled+ - What causes a run-time error when using
$value$plusargs
Syntax
$test$plusargs (user_string)
$value$plusargs (user_string, variable)
$test$plusargs: Checking Whether an Argument Was Passed
$test$plusargs is used when you just need to know whether an argument was passed, without needing a value. It searches every plusarg given on the command line, and returns a non-zero integer if any one of them starts with the string you provided — otherwise it returns zero. A variable can supply the string instead of a literal, and any null characters in it are ignored. The match is case-sensitive.
module tb;
initial begin
if ($test$plusargs ("STANDBY"))
$display ("STANDBY argument is found ...");
if ($test$plusargs ("Standby"))
$display ("Standby argument is also found ...");
if ($test$plusargs ("STAND"))
$display ("STAND substring is found ...");
if ($test$plusargs ("S"))
$display ("Some string starting with S found ...");
if ($test$plusargs ("T"))
$display ("Some string containing T found ...");
if ($test$plusargs ("STAND_AT_EASE"))
$display ("Can't stand any longer ...");
if ($test$plusargs ("SUNSHADE"))
$display ("It's too hot today ...");
if ($test$plusargs ("WINTER"))
$display ("No match.. ");
end
endmodule
Run with the run-time argument +STANDBY:
ncsim> run STANDBY argument is found ... STAND substring is found ... Some string starting with S found ... ncsim: *W,RNQUIE: Simulation is complete.
Only three of the eight checks match. "STANDBY" matches exactly. "STAND" and "S" both match too, because STANDBY (the actual plusarg on the command line) starts with each of them — the matching direction is "does a supplied plusarg begin with my string," not the other way around. "Standby" fails despite being spelled almost identically, purely because of the case difference. "T" fails because STANDBY doesn't start with T at all, even though it contains one.
Quick Check
Q: Still running with +STANDBY, would $test$plusargs("BY") return true?
Show Answer
No. STANDBY doesn't start with "BY" — it ends with it. $test$plusargs only matches from the beginning of the plusarg, not anywhere within it.
$value$plusargs: Extracting a Value
$value$plusargs searches the same way, but also extracts a value into a variable. If a plusarg matches, it returns a non-zero value and stores the result in the variable provided; if nothing matches, it returns 0 and leaves the variable untouched. The user_string takes the form "plusarg_string format_string", where the format specifier works like $display's — and critically, it controls how the plusarg's raw text gets reinterpreted, not just how it's printed afterward.
| Format Specifier | Description |
|---|---|
| %d | Decimal conversion |
| %o | Octal conversion |
| %h, %x | Hexadecimal conversion |
| %b | Binary conversion |
| %e | Real exponential conversion |
| %f | Real decimal conversion |
| %g | Real decimal or exponential conversion |
| %s | String (no conversion) |
// Note FS : Format Specifier
module tb;
initial begin
string var1, var2;
bit [31:0] data;
if ($value$plusargs ("STRING=%s", var1))
$display ("STRING with FS has a value %s", var1);
if ($value$plusargs ("NUMBER=%0d", data))
$display ("NUMBER with %%0d has a value %0d", data);
if ($value$plusargs ("NUMBER=%0h", data))
$display ("NUMBER with %%0h has a value %0d", data);
if ($value$plusargs ("NUMBER=%s", data))
$display ("NUMBER with %%s has a value %0d", data);
if ($value$plusargs ("STRING=", var1))
$display ("STRING without FS has a value %s", var1);
if ($value$plusargs ("+STRING=%s", var1))
$display ("STRING with + char has a value %s", var1);
`ifdef RUNTIME_ERR
if ($value$plusargs ("STRING=%0d", var2))
$display ("STRING with %%0d has a value %s", var2);
`endif
end
endmodule
There should never be a space between the user string, =, and the value in the command-line expression itself. Here's what different run-time arguments produce:
+STRING=Joey
"Joey" can be passed with or without quotes:
ncsim> run STRING with FS has a value Joey STRING without FS has a value Joey ncsim: *W,RNQUIE: Simulation is complete.
+NUMBER=100
The same text, "100", gets reinterpreted completely differently depending on the format specifier used to extract it:
ncsim> run NUMBER with %0d has a value 100 NUMBER with %0h has a value 256 NUMBER with %s has a value 3223600 ncsim: *W,RNQUIE: Simulation is complete.
With %0h, the characters "100" are read as hex digits, not decimal — hex 100 is 1×16² + 0×16¹ + 0×16⁰ = 256 in decimal, exactly what's printed. With %s, there's no numeric parsing at all — the raw ASCII bytes of the string "100" (0x31, 0x30, 0x30) are packed directly into the 32-bit data variable as-is, giving the bit pattern 0x313030, which read back out as a plain decimal number is 3,223,600. The format specifier isn't just choosing how to print the value — it's choosing how the plusarg's text gets turned into bits in the first place.
Quick Check
Q: If the argument were +NUMBER=20 instead, what would $value$plusargs("NUMBER=%0h", data) report as a decimal value?
Show Answer
32. Hex 20 is 2×16¹ + 0×16⁰ = 32 in decimal — the same hex-reinterpretation happening with 256 above, just with different digits.
+STRING (no = or value)
Without the =, nothing is recognized at all:
ncsim> run ncsim: *W,RNQUIE: Simulation is complete.
+STRING= (empty value)
The argument's value is an empty string:
ncsim> run STRING with FS has a value STRING without FS has a value ncsim: *W,RNQUIE: Simulation is complete.
++STRING=Joey (doubled +)
The simulator only treats the first + character as the marker that begins a plusarg — everything after it, up to the =, becomes the literal user string. So ++STRING=Joey is actually the plusarg +STRING=Joey, which is exactly what the $value$plusargs("+STRING=%s", var1) call above was searching for:
ncsim> run STRING with + char has a value Joey ncsim: *W,RNQUIE: Simulation is complete.
+STRING=1234
Even a number is parsed as a plain string by default, with no numeric conversion:
ncsim> run STRING with FS has a value 1234 STRING without FS has a value 1234 ncsim: *W,RNQUIE: Simulation is complete.
What Happens With a Mismatched Format?
If the user string matches, but the format specifier doesn't fit the data actually being passed in, you get a run-time error — not a compile-time one. Compiling with +define+RUNTIME_ERR and running with +STRING=Joey:
ncsim> run
STRING with FS has a value Joey
STRING without FS has a value Joey
if ($value$plusargs ("STRING=%0d", var2))
|
ncsim: *E,SYSFMT (./testbench.sv,27|22): $value$plusargs -- value found 'Joey' for plusarg 'STRING=' does not match specified format.
STRING with %0d has a value
ncsim: *W,RNQUIE: Simulation is complete.
"Joey" can't be interpreted as a decimal number, so the %0d-formatted call fails at run time, well after compilation succeeded — this class of mistake is only caught once the simulation actually runs with that specific argument.
Where Are Plusargs Used?
Plusargs are the standard way a regression script controls a testbench without recompiling it for every run — passing a random seed, selecting which named test scenario to run, or turning on extra debug output, all decided at the command line rather than baked into the compiled testbench.
Common Beginner Mistakes
Mistake #1: Assuming $test$plusargs needs an exact match
As the example shows, $test$plusargs("S") matches +STANDBY, not just an argument that's literally +S. Why? It's a prefix check on whatever plusargs were actually passed, which can produce unexpected matches if your check string happens to be a prefix of something else on the command line too.
Mistake #2: Forgetting plusargs are case-sensitive
$test$plusargs("Standby") fails against +STANDBY purely due to case. Why? There's no case-insensitive matching built in — the string you check against has to match the actual casing used on the command line.
Mistake #3: Mismatching the format specifier to the actual data
As the RUNTIME_ERR example shows, passing a non-numeric value where a numeric format specifier is expected compiles fine and only fails when that specific line actually executes. Why? Always make sure the format specifier matches what the command-line value will actually contain, since this class of bug won't show up until run time, and only with the exact argument that triggers it.
Mistake #4: Adding spaces around the = in a plusarg
A command-line argument like +NUMBER = 100 (with spaces) won't be recognized the way +NUMBER=100 is. Why? The plusarg syntax requires the string, =, and value to be written with no spaces between them — any space breaks the parsing.
Practice Exercise
Challenge: Predict the Matches
Given the run-time argument +VERBOSE, which of these $test$plusargs calls would return true: $test$plusargs("VERBOSE"), $test$plusargs("VERB"), $test$plusargs("verbose"), $test$plusargs("OSE")?
Hint
Check each string against two things: does it match the start of VERBOSE exactly, and does the case match too?
Solution
"VERBOSE" and "VERB" both return true — VERBOSE starts with both of them, and the case matches. "verbose" fails on case (lowercase versus the actual uppercase VERBOSE). "OSE" fails because VERBOSE doesn't start with "OSE", even though it appears at the end of the word.
Summary
In this tutorial, you learned:
$test$plusargschecks whether any command-line plusarg starts with a given string, with no value extraction — it's case-sensitive and matches by prefix$value$plusargsextracts a value into a variable, using a format specifier that controls how the plusarg's raw text is reinterpreted into bits- The exact same text can produce very different numeric results depending on the format specifier —
%hreads digits as hex, while%spacks raw ASCII bytes with no numeric parsing at all - A missing
=, an empty value, or spaces around the=all change how (or whether) a plusarg is recognized - A format specifier that doesn't match the actual data produces a run-time error, not a compile-time one
Continuing the Testbench Components Chapter
Continue with SystemVerilog file operations.