Friday, August 3, 2012

SystemVerilog variable argument display (printf)

Ah, the joy of perfection.

Any C / C++ programmers now programming in SystemVerilog must feel pretty constrained by language limitations. One of the frustrations that I felt today was the lack of variable argument (varargs) support. I wanted to be able to overload a print function (ie write my own $display) and selectively print or not print based on debug levels or the like. Actually... what I wanted was to store all the debug prints between runs, and only print them in the case of an error, but that's besides the point. I needed a way to overload the $display function.

Here is what I've finally come up with: (be ready, it's wild)

$display("Print with no arguments");
$display("Print with 1 argument %t", $time());
$display("Print with 2 arguments %t, %d", $time(), 5);
$display("Print with 3 arguments %t, %d, %s", $time(), 5, "debug");
etc...

I wanted this to become:
DEBUG_PRINT("Print with no arguments");
DEBUG_PRINT("Print with 1 argument %t", $time());
DEBUG_PRINT("Print with 2 arguments %t, %d", $time(), 5);
DEBUG_PRINT("Print with 3 arguments %t, %d, %s", $time(), 5, "debug");
etc...
Is it possible??

Yes! (at least until someone points out a flaw)

The solution is based on macros that can have variable number of arguments, and the preprocessor allowing ifdef on substituted strings

function void my_debug(string s);
...
endfunction

`define DELIM
`define DEBUG_PRINT(p0, p1=ELIM, p2=ELIM, p3=ELIM, p4=ELIM, p5=ELIM) \
`ifdef D``p1 \
my_debug($psprintf(p0)); \
`else \
`ifdef D``p2 \
my_debug($psprintf(p0, p1)); \
`else \
`ifdef D``p3 \
my_debug($psprintf(p0, p1, p2)); \
`else \
`ifdef D``p4 \
my_debug($psprintf(p0, p1, p2, p3)); \
`else \
`ifdef D``p5 \
my_debug($psprintf(p0, p1, p2, p3, p4)); \
`else \
my_debug($psprintf(p0, p1, p2, p3, p4, p5)); \
`endif \
`endif \
`endif \
`endif \
`endif

This solution works on Cadence Incisive 12.1

Tell me what you think of this solution,
Nachum