Plusargs in SystemVerilog: $test$plusargs and $value$plusargs (Tested Examples)
In short: Plusargs are switches you add to the simulator’s run command, each starting with +, like +DEBUG or +SEED=42. Your code reads them at run time, so you can change a test’s behaviour without recompiling. $test$plusargs("DEBUG") tells you whether a switch is there. $value$plusargs("SEED=%d", seed) reads the value after it. Always set a default first, because a missing switch leaves the variable alone.
Here’s what it looks like on a real run command:
./simv +TEST=axi_burst_test +COUNT=250 +DEBUG
Same compiled testbench. Different test, different count, debug on.
No rebuild.
Think of it like a café order
The kitchen is built once: that’s your compiled testbench. Every customer hands over an order slip: that’s the command line.
- **
$test$plusargsis the cook asking “is it on the slip?”** “No onions?” Yes or no. - **
$value$plusargsis the cook asking “what does the slip say?”** “Size?” Large. - The default is the house standard. If the slip says nothing about size, you get a regular.
You never rebuild the kitchen to serve a different order.
Example 1: is the switch there?
// $test$plusargs: is a switch on the command line?
module tb;
initial begin
if ($test$plusargs("DEBUG"))
$display("DEBUG is ON");
else
$display("DEBUG is off");
$finish;
end
endmodule
Output (tested on Icarus Verilog 12.0 and Verilator 5.020, 11 Nov 2026):
run with nothing -> DEBUG is off
run with +DEBUG -> DEBUG is ON
run with +DEBUG_LEVEL=3 -> DEBUG is ON <- surprise
That third line is the first surprise. More on it below.
Example 2: read a value, keep a default
// $value$plusargs: read a value, keep a default if it's missing
module tb;
string test_name = "smoke_test"; // defaults first
int count = 10;
bit [31:0] addr = 32'h1000;
initial begin
if ($value$plusargs("TEST=%s", test_name)) $display("TEST from command line");
if ($value$plusargs("COUNT=%d", count)) $display("COUNT from command line");
if ($value$plusargs("ADDR=%h", addr)) $display("ADDR from command line");
$display("test_name=%s count=%0d addr=0x%0h", test_name, count, addr);
$finish;
end
endmodule
Output, run with nothing (Icarus and Verilator):
test_name=smoke_test count=10 addr=0x1000
Output, run with +TEST=axi_burst_test +COUNT=250 +ADDR=dead_beef:
TEST from command line
COUNT from command line
ADDR from command line
test_name=axi_burst_test count=250 addr=0xdeadbeef
- No switches: every variable keeps its default. Nothing breaks.
%hreaddead_beefas hex, even with the_in the middle.- The format letter decides how the text is read:
%ddecimal,%hhex,%bbinary,%ooctal,%sstring,%freal.
Three surprises worth knowing
// Three surprises worth knowing
module tb;
int n = -1;
initial begin
// 1. Prefix match: "DEBUG" also matches +DEBUG_LEVEL=3
$display("test$plusargs(\"DEBUG\") = %0d", $test$plusargs("DEBUG"));
// 2. Given twice: which one wins?
void'($value$plusargs("N=%d", n));
$display("N = %0d", n);
// 3. Case matters
$display("test$plusargs(\"debug_level\") = %0d", $test$plusargs("debug_level"));
$finish;
end
endmodule
Output, run with +DEBUG_LEVEL=3 +N=5 +N=9 (Icarus and Verilator):
test$plusargs("DEBUG") = 1
N = 5
test$plusargs("debug_level") = 0
- Prefix match.
$test$plusargs("DEBUG")checks whether any switch starts withDEBUG. So+DEBUG_LEVEL=3counts. Picture a cook who sees “No onion rings” and skips the onions too. Fix: give switches names that aren’t prefixes of each other, like+DBG_ONand+DBG_LEVEL=3. - First one wins. With
+N=5 +N=9, you get 5. Handy to know when a regression script adds its own switch after yours. - Case matters.
debug_levelis notDEBUG_LEVEL.

And a fourth, from the bad-input test. Running example 2 with +COUNT=abc:
| Simulator | Returned | count became |
|---|---|---|
| Icarus 12.0 | 1 (“found it”) | x, plus a warning |
| Verilator 5.020 | 1 (“found it”) | 0, no warning |
Both say “found it” and both throw away your default of 10. $value$plusargs checks that the switch is there, not that the value makes sense. If a value matters, check its range after reading it.
$test$plusargs vs $value$plusargs at a glance
$test$plusargs | $value$plusargs | |
|---|---|---|
| Asks | Is the switch there? | What value comes after it? |
| Command line | +DEBUG | +SEED=42 |
| Code | $test$plusargs("DEBUG") | $value$plusargs("SEED=%d", seed) |
| Returns | 1 if found, else 0 | 1 if found, else 0 |
| Writes a variable | No | Yes, only when found |
| Café picture | “No onions on the slip?” | “What size does the slip say?” |
| Good for | On/off modes, debug prints | Test name, seed, counts, addresses, timeouts |
Working example 4: a list from the command line
$value$plusargs reads one value into one variable. For a list, number the switches and read them in a loop.
// Read a numbered list: +SLAVE_0=1 +SLAVE_1=3 +SLAVE_2=7
module tb;
int slaves[$];
initial begin
int v, i;
i = 0;
// %%d becomes %d after $sformatf, so $value$plusargs sees "SLAVE_0=%d"
while ($value$plusargs($sformatf("SLAVE_%0d=%%d", i), v)) begin
slaves.push_back(v);
i++;
end
$display("got %0d slaves", slaves.size());
foreach (slaves[k]) $display(" slave[%0d] = %0d", k, slaves[k]);
$finish;
end
endmodule
Output, run with +SLAVE_0=1 +SLAVE_1=3 +SLAVE_2=7 (Icarus and Verilator):
got 3 slaves
slave[0] = 1
slave[1] = 3
slave[2] = 7
Run with nothing and it prints got 0 slaves. The loop stops at the first missing number, like reading numbered tickets until one is missing.
The %%d is the trick: $sformatf turns %% into a single %, so the format string ends up as SLAVE_0=%d.
Working example 5: pick an enum by name
// Pick an enum by name: +MODE=STRESS
typedef enum {NORMAL, LOW_POWER, STRESS} mode_e;
module tb;
mode_e mode = NORMAL; // default
initial begin
string s;
mode_e m;
bit found;
if ($value$plusargs("MODE=%s", s)) begin
m = m.first();
repeat (m.num()) begin // walk every name in the enum
if (m.name() == s) begin mode = m; found = 1; end
m = m.next();
end
if (!found) $error("Unknown MODE '%s', keeping %s", s, mode.name());
end
$display("mode = %s", mode.name());
$finish;
end
endmodule
Output (Icarus and Verilator):
+MODE=STRESS -> mode = STRESS
+MODE=stress -> ERROR: Unknown MODE 'stress', keeping NORMAL
- Read the name as a string, then walk the enum until a name matches. No casts, no magic numbers on the command line.
- A typo gets a loud error instead of silently running the wrong mode. (Verilator also stops the run at
$error; Icarus prints the error and carries on.)
Working example 6: a parameter can’t change, a variable can
// A parameter can't change at run time. A real variable can.
module tb;
parameter real BASE_MHZ = 1500.0; // fixed when the design is built
real step_mhz = 40.0; // run-time knob
initial begin
void'($value$plusargs("STEP_MHZ=%f", step_mhz));
$display("BASE_MHZ = %0.1f, step_mhz = %0.1f", BASE_MHZ, step_mhz);
$finish;
end
endmodule
Output, run with +STEP_MHZ=50 (Icarus and Verilator):
BASE_MHZ = 1500.0, step_mhz = 50.0
A parameter is printed on the kitchen wall when it’s built. A plusarg is written on today’s order slip. You can’t change the wall with a slip.
Plusargs vs ifdef
ifdef / +define+ | Plusargs | |
|---|---|---|
| Decided at | Compile time | Run time |
| Change it means | Recompile | Just rerun |
Can add or remove code, ports, assigns | Yes | No |
| Can change values and choices | Yes | Yes |
| Café picture | Rebuilding the kitchen | A different order slip |
Rule of thumb: if it changes the structure (what hardware or code exists), use ifdef. If it changes behaviour (which test, how many, how loud), use a plusarg. More in ifdef vs plusargs.

Plusargs you already use in UVM
+UVM_TESTNAME=my_testpicks the test thatrun_test()builds.+UVM_VERBOSITY=UVM_HIGHturns up the messages.- UVM also has
uvm_cmdline_processor, which can return every match of a repeated switch (+SLAVE=1 +SLAVE=3), something plain$value$plusargscan’t do. - The random seed is usually a simulator switch, not your own plusarg. On VCS, for example, it’s
+ntb_random_seed=42. Check your simulator’s manual for the exact name.
Common mistakes (and the fix for each)
These come from real Verification Academy threads, retold in my own words with links.
1. A match string that’s too short, or too long
- What happened: someone checked
$test$plusargs("ARG1")and found it was also true for+ARG1232(Verification Academy). In 2016 another engineer was surprised that$test$plusargs("CPU0_LOAD="), with the=, only matched+CPU0_LOAD=...and not+CPU0_LOAD(Verification Academy, 2016). - Why: the string is matched against the start of each switch, character for character,
=included. - Fix: pick switch names that aren’t prefixes of each other, and write the match string exactly as it starts on the command line.
2. Trying to set a parameter at run time
- What happened: in 2020 an engineer wanted
+base_freq=1.7to change a PLL model’sparameter(Verification Academy, 2020). - Why: a parameter is fixed when the design is built. Plusargs arrive after that.
- Fix: use a
realvariable and%f, like example 6.
3. Reading a whole list in one go
- What happened: someone tried
$value$plusargs("target_slaves=%p", list)with+target_slaves={1,3}and got “Second argument must be a modifiable variable” (Verification Academy). - Why:
%pisn’t a plusarg format, and$value$plusargswrites one variable, not an array. - Fix: numbered switches in a loop (example 4), the same approach Dave Rich suggested in a 2020 thread. Or
uvm_cmdline_processorin UVM.
4. Setting an enum from the command line
- What happened: in 2024 an engineer wanted both
+COLOR=yellowand+COLOR=3to work for an enum (Verification Academy, 2024). In another thread, every enum config came out as its first value no matter what was passed (Verification Academy). - Why: there’s no enum format letter. You read either a string or a number, then convert it yourself.
- Fix: read a string and match it against the enum’s names (example 5), or read an
intand cast it.
5. Using a plusarg to add or remove hardware
- What happened: in 2020 an engineer put
assignstatements insideif (!$test$plusargs("HB_CONN_DISABLE"))to drop a connection for one test, and got a genvar error (Verification Academy, 2020). - Why: an
ifaroundassigns is a generate block, decided when the design is built. Plusargs aren’t known yet. - Fix: structure goes in
ifdef. Plusargs choose behaviour inside code that already exists.
Every idea on this page, in one picture
| Idea | Picture it as |
|---|---|
| Compiled testbench | A café kitchen, built once |
| Plusargs | The order slip each customer hands over |
$test$plusargs | “Is ‘no onions’ on the slip?” |
$value$plusargs | “What size does the slip say?” |
| Default value | The house standard when the slip says nothing |
| Prefix match | Seeing “no onion rings” and skipping the onions too |
| First one wins | The cook reads the slip top to bottom and stops at the first match |
| Bad number still “found” | The slip says “size: banana”; the cook still thinks a size was given |
| Numbered list | Reading numbered tickets until one is missing |
| Parameter vs plusarg | The menu on the wall vs today’s slip |
ifdef | Rebuilding the kitchen |
FAQ
What is the difference between $test$plusargs and $value$plusargs?
$test$plusargs only checks whether a switch is there. $value$plusargs also reads the value after it into a variable.
Do I need to recompile to change a plusarg?
No. That’s the point. Just rerun with different switches.
What happens if the plusarg is missing?
$value$plusargs returns 0 and leaves your variable untouched, so set a default before calling it.
How do I pass a string with a plusarg?
+TEST=axi_burst_test, read with %s into a string variable.
Can I read a hex value?
Yes, use %h: +ADDR=dead_beef reads as 0xdeadbeef.
Can a plusarg change a parameter?
No. Parameters are fixed at build time. Use a variable instead.
References
- IEEE 1800-2017, SystemVerilog standard: section 21.6 (command line input:
$test$plusargsand$value$plusargs). - Verification Academy forum threads linked in the mistakes section (accessed 11 Nov 2026).
- All examples tested on Icarus Verilog 12.0 and Verilator 5.020 on 11 Nov 2026.
What to read next
- ifdef vs plusargs: compile-time vs run-time switches
- rand_mode vs constraint_mode: pair a plusarg with these to switch randomization per test
- SystemVerilog dist: := vs :/: drive weights from a plusarg knob
Part of the learning path SystemVerilog for verification
