Part 7 · Advanced & Integration · Intermediate
$test$plusargs & $value$plusargs
Existence vs value plusargs, format strings, default patterns, return-value checking, and the knob registry idiom.
Two system functions, two jobs
$test$plusargs("NAME") answers one question — was +NAME given on the command line — and returns 1 or 0. $value$plusargs("NAME=%d", var) goes further: it matches the prefix, parses the rest through a format string into your variable, and returns nonzero only when the plusarg was present and the parse succeeded. Existence flags switch features on; value plusargs carry numbers, strings, and hex values into the simulation.
module tb_top;
int num_txns = 100; // default lives with the declaration
string test_name = "smoke";
bit [31:0] base_addr = 32'h1000_0000;
initial begin
// Existence check — flag style
if ($test$plusargs("ENABLE_COVERAGE"))
$display("coverage collection ON");
// Value parse — %d decimal, %s string, %h hex
if (!$value$plusargs("NUM_TXNS=%d", num_txns))
$display("NUM_TXNS not given, using default %0d", num_txns);
void'($value$plusargs("TESTNAME=%s", test_name));
void'($value$plusargs("BASE_ADDR=%h", base_addr));
$display("test=%s txns=%0d base=%h", test_name, num_txns, base_addr);
end
endmodule
// Run: simv +ENABLE_COVERAGE +NUM_TXNS=500 +TESTNAME=stress +BASE_ADDR=2000_0000
// Note: $test$plusargs does PREFIX matching — "+ENABLE_COVERAGE_X"
// also satisfies $test$plusargs("ENABLE_COVERAGE"). Name knobs carefully.The defaults pattern
Initialize the variable to its default, then let $value$plusargs overwrite it only on a successful parse. The return value tells you which happened — check it when the knob is mandatory, and void'() it when the default is acceptable, so lint stays quiet without hiding intent.
Scanning multiple related plusargs
Format strings parse one value per call, but you can loop over a family of numbered plusargs to accept a variable-length list — error injection points, channel enables, per-port delays. The loop stops at the first missing index.
// Accept +ERR_ADDR0=... +ERR_ADDR1=... up to 8 injection points
bit [31:0] err_addr[$];
initial begin
bit [31:0] val;
string key;
for (int i = 0; i < 8; i++) begin
key = $sformatf("ERR_ADDR%0d=%%h", i); // %%h → literal %h in result
if ($value$plusargs(key, val))
err_addr.push_back(val);
else
break; // first gap ends the list
end
$display("%0d error injection points loaded", err_addr.size());
end
// simv +ERR_ADDR0=1000 +ERR_ADDR1=2FF0 +ERR_ADDR2=4000The knob registry idiom
Mature testbenches do not sprinkle plusarg calls through drivers and monitors. They declare every knob in one registry — name, default, description — parse the command line once at time zero, and print the resolved table into the log. The log of every run then documents its own configuration, and a typo in a plusarg name is visible instead of silently ignored.
class knob_registry;
int num_txns = 100;
int verbosity = 1;
string test_name = "smoke";
bit enable_cov = 0;
function void parse();
void'($value$plusargs("NUM_TXNS=%d", num_txns));
void'($value$plusargs("VERBOSITY=%d", verbosity));
void'($value$plusargs("TESTNAME=%s", test_name));
enable_cov = $test$plusargs("ENABLE_COVERAGE");
print();
endfunction
function void print();
$display("=== KNOBS ===========================");
$display(" TESTNAME = %s", test_name);
$display(" NUM_TXNS = %0d", num_txns);
$display(" VERBOSITY = %0d", verbosity);
$display(" ENABLE_COVERAGE = %0b", enable_cov);
$display("=====================================");
endfunction
endclassKNOB FLOW
command line +TESTNAME=stress +NUM_TXNS=500
│
▼ parsed ONCE at time 0
knob_registry { test_name="stress", num_txns=500, defaults... }
│
├──► printed to log (run is self-documenting)
│
└──► read by env / driver / sequences via the registry handle
(no component calls $value$plusargs itself)Key takeaways
$test$plusargs checks existence; $value$plusargs parses a value and reports success via its return.
Initialize defaults at declaration and let a successful parse overwrite them — the cleanest default pattern.
Plusarg matching is prefix-based — choose knob names that are not prefixes of each other.
Centralize parsing in a knob registry, parse once, and print the resolved configuration into every log.
Common pitfalls
Ignoring the $value$plusargs return on a mandatory knob — the test silently runs with the default.
Prefix collisions like +DUMP and +DUMP_START — $test$plusargs("DUMP") matches both.
Parsing a hex command-line value with %d — parse fails or truncates; match the format to how users type it.
Calling $value$plusargs inside hot loops or every transaction — wasteful and scatters configuration; parse once at time 0.
Interview angle
Expect: difference between the two plusarg functions and their return values; how to give a knob a default (initialize, then conditional overwrite); the prefix-matching gotcha; and the architecture question — where should plusarg parsing live in a UVM environment (one config/knob class at time zero, not in components).