For a week, the nightly regression reported 400 green runs of 400 different tests. Then someone opened a log: every run said Running test dma_smoke_test.... The launcher had been edited to write +UVM_TESTNAM= with the last letter missing. UVM never complained, because an argument it does not recognise is not an error; it is just not an instruction, so every run fell back to the name passed to run_test(). Which command-line mistakes does UVM catch, which does it swallow, and how do you make a bench that swallows none?
Every simulator argument starting with + is a plusarg. Two things read them. Any SystemVerilog code can call $test$plusargs and $value$plusargs (IEEE 1800), which ask the simulator directly. UVM adds the uvm_cmdline_processor, a singleton that copies the whole argument list once, at start-up, through the library's DPI code, and offers search methods on that copy. The library's own plusargs (+UVM_TESTNAME, +uvm_set_verbosity and the rest) are read through it at fixed moments.
Picture the command line as an order slip clipped to a kitchen ticket. Each station reads only the lines it knows; a line nobody knows is ignored, not refused. So a misspelt name is silent, while a misspelt value of a known name is usually caught by the station that owns it.
The order matters. Command-line overrides and config are applied in uvm_root's build_phase, after the test object exists but before the test's own build_phase. A type override that the test sets for the same type in its build_phase therefore comes later and replaces the command line's (the override that lost to the test); a command-line config set, made from uvm_root, outranks the test's build-phase set.
Method | Returns | Matching |
|---|---|---|
| number of matches; | prefix |
| number of matches; | prefix |
| number of matches; | prefix, or a regular expression between slashes |
| all arguments / those starting with | none |
Get the handle with uvm_cmdline_processor::get_inst(). Every match is a prefix match on the raw argument, and that is the first typing trap, shown in the run below.
Plusarg | Syntax | Notes (from the 2020.3 source) |
|---|---|---|
|
| Beats |
|
| Default verbosity of every component. A bad value is |
|
|
|
|
|
|
|
|
|
|
|
|
|
| Stop after |
| no value | Every phase transition; every raise, drop and propagation. |
| no value | Every config_db set and get (with failed lookups); |
|
| Type names, like |
|
| The path starts at |
|
| Set from |
| no value | Print all arguments; drop the banner. |
The upper-case spellings +UVM_SET_TYPE_OVERRIDE, +UVM_SET_INST_OVERRIDE and +UVM_SET_CONFIG_INT are accepted too. +UVM_TESTNAME is not: +uvm_testname= is just another unknown argument. The factory rules behind the override plusargs are in Command-line overrides; the timeout itself is in run_test and timeouts.
A project's own plusargs (+DMA_JOBS=5) need what UVM's lack: a declared list, a type, a default and an error for anything unknown. Parse them once, in the base test's build_phase, into a config object that the rest of the bench reads; never call the processor per transaction.
// snippet: scratch run alongside the DMA bench (needs the library's DPI code)
class dma_plusargs extends uvm_object;
`uvm_object_utils(dma_plusargs)
int unsigned jobs = 3; // +DMA_JOBS=<n>
int unsigned skip[$]; // +DMA_SKIP=<job>, repeatable
bit no_irq; // +DMA_NO_IRQ
string known[$] = '{"+DMA_JOBS=", "+DMA_SKIP=", "+DMA_NO_IRQ"};
function new(string name = "dma_plusargs");
super.new(name);
endfunction
function void parse();
uvm_cmdline_processor clp = uvm_cmdline_processor::get_inst();
string v, vals[$], all[$];
if (clp.get_arg_value("+DMA_JOBS=", v)) jobs = v.atoi();
void'(clp.get_arg_values("+DMA_SKIP=", vals));
foreach (vals[i]) skip.push_back(vals[i].atoi());
no_irq = clp.get_arg_matches("+DMA_NO_IRQ", all) > 0;
// Anything else that starts with +DMA_ is a typo: stop before time moves.
void'(clp.get_arg_matches("+DMA_", all));
foreach (all[i]) begin
bit ok = 0;
foreach (known[k])
if (all[i].substr(0, known[k].len() - 1) == known[k]) ok = 1;
if (!ok) `uvm_fatal("PLUSARG", {"unknown project plusarg ", all[i]})
end
endfunction
endclassFrom the DPI scratch build, with +DMA_JOBS=5 +DMA_SKIP=2 +DMA_SKIP=4 +uvm_set_config_int=uvm_test_top,n_jobs,5:
UVM_INFO @ 0: reporter [UVM_CMDLINE_PROC] Applying config setting from the command line: +uvm_set_config_int=uvm_test_top,n_jobs,5
UVM_INFO adv_tb.sv(245) @ 0: uvm_test_top [PA] config_db#(int) get n_jobs: 0 (value 0)
UVM_INFO adv_tb.sv(247) @ 0: uvm_test_top [PA] config_db#(uvm_bitstream_t) get n_jobs: 1 (value 5)
UVM_INFO adv_tb.sv(251) @ 0: uvm_test_top [PA] get_arg_value(+DMA_JOBS=) found 1, value '5'
UVM_INFO adv_tb.sv(253) @ 0: uvm_test_top [PA] get_arg_value(+DMA_JOB) found 1, value 'S=5'
UVM_INFO adv_tb.sv(255) @ 0: uvm_test_top [PA] get_arg_values(+DMA_SKIP=) found 2: '{"2", "4"}Then the silent and the loud cases, each from its own run:
+UVM_TESTNAM=dma_rand_test UVM_INFO @ 0: reporter [RNTST] Running test dma_smoke_test...
+UVM_TESTNAME=dma_smok_test UVM_FATAL @ 0: reporter [INVTST] Requested test from command line +UVM_TESTNAME=dma_smok_test not found.
+UVM_VERBOSITY=UVM_HIHG UVM_WARNING ... [ILLVERB] Illegal verbosity value 'UVM_HIHG', converted to default of UVM_MEDIUM.
+uvm_set_inst_override=axis_driver,axis_truncating_driver,env.in_agt.drv
UVM_INFO @ 0: reporter [INSTOVR] Applying instance override from the command line: ...
UVM_INFO dma_tb.sv(411) @ 655000: uvm_test_top.env.sb [SB] 8 words matched, 0 mismatched
+DMA_JOBZ=5 (with the registry) UVM_FATAL adv_tb.sv(309) @ 0: reporter [PLUSARG] unknown project plusarg +DMA_JOBZ=5A misspelt plusarg name is silent; a misspelt value of a known name is usually caught.
The instance override was "applied" and matched nothing: the path lacks uvm_test_top., so the driver was never replaced and the smoke test passed. With the full path, the same run prints TLAST forced on beat 3 and fails on STATUS.
The registry turns a project typo into a fatal at time 0, instead of a week of runs with the default.
A bitstream, not an int. +uvm_set_config_int stores a uvm_bitstream_t. It is seen by uvm_config_db#(uvm_bitstream_t) and by field-macro auto-config, not by uvm_config_db#(int)::get (the run above: 0 versus 1). Read command-line ints as uvm_bitstream_t, or use your own plusarg.
Prefix matching. get_arg_value("+DMA_JOB", v) matched +DMA_JOBS=5 and returned S=5. Always search with the =.
A number with no unit. +UVM_TIMEOUT is read in the UVM package's time unit: in the scratch build +UVM_TIMEOUT=3000000,NO fired at @ 3000000000 (3 ms in ps) and 3000,NO at 3 µs. With ,YES the bench's dma_timeout_test replaced it with its own set_timeout(20us); with ,NO the library refused (NOTIMOUTOVR).
Commas split fields. Values for +uvm_set_config_string cannot contain a comma.
Actions do not hide errors from the summary. +uvm_set_action=...,SB_EMPTY,UVM_ERROR,UVM_DISPLAY still left UVM_ERROR : 1 in dma_early_end_test, because the server counts every severity; UVM_COUNT only feeds the quit count. +uvm_set_severity=uvm_test_top.env.sb,SB_EMPTY,UVM_ERROR,UVM_INFO made the same broken test print TEST VERDICT: PASS.
The hook's default-test week is one failure. The quieter one is the +uvm_set_severity line above: a single argument in a regression file turned an empty-scoreboard error into an info, and the verdict server printed PASS for a test that compared nothing, the same false pass the bench's dma_false_pass_test builds in code. It is easy to add during bring-up and easy to forget.
Senior teams treat the command line as part of the test plan: the launcher checks +UVM_TESTNAME against the compiled test list (or compares the RNTST line), project plusargs come from the registry, +uvm_set_severity and +uvm_set_action are banned in signoff runs, and every log starts with +UVM_DUMP_CMDLINE_ARGS output.
+UVM_TIMEOUT=500,NO, and the test's build_phase calls uvm_root::get().set_timeout(2ms, 1). The test hangs. When does it end, and what does the log say first?The command line is applied in run_test before the test is built, and NO locks it. The test's set_timeout prints NOTIMOUTOVR and is ignored. The run ends with PH_TIMEOUT after 500 units of the UVM package's time unit: 500 ns if that unit is ns, as in the course build, which is probably far shorter than intended.
+uvm_set_verbosity=uvm_test_top.env.*,_ALL_,UVM_HIGH,run is on the command line. Does a UVM_HIGH message printed by the scoreboard in connect_phase appear?No. The setting names the run phase, so it is applied when the run phase starts. A message in connect_phase still sees the old verbosity. Use build (or time,0) as the phase field to apply it from construction.
+DMA_SEED_MODE=fixed is read with $value$plusargs("DMA_SEED_MODE=%s", s) in a monitor's write() function. Name two problems.It searches the command line on every transaction, and no registry sees it, so +DMA_SEED_MOD=fixed would be silently ignored. Parse it once in the base test into a config object, validated against the declared list.
Next: the most important argument on the command line is the one that is not a plusarg at all: the seed. Random stability and seeds shows how UVM turns one number into every random value in the run.