Preview
``, `", `\`"
then macro rich code may seem a little confusing, initimidating even. The examples in here will demystify what these symbols mean and how to use them.
Macros are code snippets created using the `define
compiler directive. They essentially have 3 parts - a name, some text and optional arguments.
`define macroname(ARGS) macrotext
At compile time every occurance of `macroname
in your code is substituted for the string macrotext
and ARGS
are variables that can be used within macrotext
.
While coding up my testbench and tests, I found myself printing out an array of bytes at several places. At various places in my code I would have snippets like this
//1
for (int ii=0; ii<numbytes; ii++) begin
if ((ii !=0) && (ii % 16 == 0))
$display("\n");
$display("0x%x ", bytearray[ii]);
end //2
for (int ii=20; ii<100; ii++) begin
if (ii % 16 == 0)
$display("\n");
$display("0x%x ", pkt[ii]);
end
Instead, I could define a macro like the following, replace 5 lines of repeated code with 1, save some typing and make my code more readable. Also, if I need to make a change to the print style, then I just have to change it in one place!
`define print_bytes(ARR, STARTBYTE, NUMBYTES) \
for (int ii=STARTBYTE; ii<STARTBYTE+NUMBYTES; ii++) begin
if ((ii != 0) && (ii % 16 == 0))
$display("\n");
$display("0x%x ", ARR[ii]);
end
// When someone reads this code, they'll know
// it prints a formatted array of bytes
`print_bytes(bytearray, 0, numbytes)
`print_bytes(pkt, 20, 80)
I like using macros especially for special print functions, something for which a `uvm_info
isn't sufficient. If all your team folks use this macro then you have consistent looking print messages and this makes simulation logs easier to read for everyone.
Here is one possible way to use macros -
<*>_utils
(print_byte_utils
, etc).macro_utils.sv
and include it in your base packageHope I've made a convincing case for them Macros.
The only rule to a macro's name is that you can use any name EXCEPT compiler directives, i.e, keywords such as `define, `ifdef, `endif, `else, `elseif, `include
etc cannot be used. If you do end up using a compiler directive by mistake, you'll get an error such as this
`define include(ARG) `"ARG.master`"
Mentor Graphics Questa ** Error: macros_one.sv(4): (vlog-2264) Cannot redefine compiler directives: `include. Synopsys VCS Error-[IUCD] Illegal use of compiler directive Illegal use of compiler directive: `include is illegal in this context. "macros_one.sv", 28 Source info: $display(`include(clock));
Also, a macro's name space is Global. So it doesn't matter if something is defined within a class or outside it, the order of compilation is what matters. Once a definition is picked up by the compiler, it can be used anywhere. If you re-define macros you'll get such a warning. Watch out for these in your logs.
** Warning: macros2.sv(7): (vlog-2263) Redefinition of macro: 'debug' (previously defined near macros2.sv(3)) . Parsing design file 'macros4.sv' Warning-[TMR] Text macro redefined macros2.sv, 7 Text macro (debug) is redefined. The last definition will override previous ones. In macros2.sv, 3, it was defined as $display("%s, %0d: %s", `__FILE__, `__LINE__, msg)
``, `", `\`"
There are 3 tokens that need some explanation
`"
- If the macrotext is enclosed in plain quotation("), it essentially becomes a string. Arguments within the double-quotes are not substituted and if the macrotext has other macros embedded, they are not expanded. The `"
(back-tick before the double-quote) overrides the meaning of "
and indicates that the macro expansion should
Let's look at example 1.1
/* Example 1.1 */
`define append_front_bad(MOD) "MOD.master"
`define append_front_good(MOD) `"MOD.master`"
program automatic test;
initial begin
$display(`append_front_bad(clock1));
$display(`append_front_good(clock1));
end
endprogram: test
CONSOLE OUTPUT
# MOD.master
# clock1.master
`append_front_bad
- accepts a argument, MOD
, and the expectation is that the macro will result in concatenation of 2 strings - MOD
and ".master"
. But because of the double quotes the output results in the string "MOD.master"
and the argument is not substituted.`append_front_good
- This is the correct version of `append_front_bad
. By using the back-tick before the double-quotes, we tell the compiler that the argument MOD
has to be substituted when the macro is expanded.``
- The double-backtick(``) is essentially a token delimiter. It helps the compiler clearly differentiate between the Argument and the rest of the string in the macro text. This is best explained with an example.
/* Example 1.2 */
`define append_front_2a(MOD) `"MOD.master`"
`define append_front_2b(MOD) `"MOD master`"
`define append_front_2c_bad(MOD) `"MOD_master`"
`define append_front_2c_good(MOD) `"MOD``_master`"
`define append_middle(MOD) `"top_``MOD``_master`"
`define append_end(MOD) `"top_``MOD`"
`define append_front_3(MOD) `"MOD``.master`"
program automatic test;
initial begin
/* Example 1.2 */
$display(`append_front_2a(clock2a));
$display(`append_front_2b(clock2b));
$display(`append_front_2c_bad(clock2c));
$display(`append_front_2c_good(clock2c));
$display(`append_front_3(clock3));
$display(`append_middle(clock4));
$display(`append_end(clock5));
end
endprogram: test
CONSOLE OUTPUT
# clock2a.master
# clock2b master
# MOD_master
# clock2c_master
# clock3.master
# top_clock4_master
# top_clock5
`append_front_2a and 2b
- The space character (' '
) and the period character ('.'
) are natural token delimiters, i.e., the compiler can clearly identify the argument in the macro text.`append_front_2c_bad
- If the argument has a non-space or non-period character attached to it, such as the underscore('_'
) in `"MOD_master`"
, then the compiler can't figure out where the argument string MOD
ends.`append_front_2c_good
the ``
creates a delimiter between MOD
and _master
and that's why MOD
is correctly identified and substituted. `append_middle
and `append_end
are 2 more examples that show this.`append_front_3
- It is ok to place a ``
before or after a natural token delimiter (i.e., space or period) but it will have no effect.
`\`"
- This results in a literal \"
when the macro is expanded. Use this if you need double quotes within your expanded macro text.
/* Example 1.3 */
`define complex_string(ARG1, ARG2) \
`"This `\`"Blue``ARG1`\`" is Really `\`"ARG2`\`"`"
program automatic test;
initial begin
$display(`complex_string(Beast, Black));
end
endprogram: test
CONSOLE OUTPUT
# This "BlueBeast" is Really "Black"
If you still feel a little shaky on this concept, don't worry! ... We'll examine a bunch of more examples further down this article and that should clear things up.
Download the example code from this section
ifdef
s within the macro definition is also allowed, nothing different about this eitherA few things to keep in mind about arguments to macros
`test2(,,)
prints out in example 2.`debug1
and `debug2
. If your argument is a string, then the decision of whether you need to enclose the argument in double-quotes depends on where the argument is substituted in the macro text. In `debug1
, MODNAME
is within quotes in the macro text hence I haven't enclosed the string program-block
in quotes while calling the macro. Whereas with `debug2
the argument passed is "program-block"
because MODNAME
appears outside quotes in the macro text. /* Example 2 */
`define test1(A) $display(`"1 Color A`")
`define test2(A=blue, B, C=green) $display(`"3 Colors A, B, C`")
`define test3(A=str1, B, C=green) \
if (A == "orange")$display("Oooo .. I received an Orange!"); \
else $display(`"3 Colors %s, B, C`", A);
`define debug1(MODNAME, MSG) \
$display(`" ``MODNAME`` >> %s, %0d: %s`", `__FILE__, `__LINE__, MSG)
`define debug2(MODNAME, MSG) \
$display(`"%s >> %s, %0d: %s`", MODNAME, `__FILE__, `__LINE__, MSG)
program automatic test;
initial begin
string str1, str2;
str1 = "gray";
str2 = "orange";
// No args provided even without default value
`test1();
`test2(,,);
// Passing some args
`test1(black);
`test2(,pink,);
// Passing a var (str1, str2) as an arg
`test3(str1,mistygreen,babypink);
`test3(str2,mistygreen,babypink);
`debug1(program-block, "this is a debug message");
`debug2("program-block", "this is a debug message");
/* The following will result in an Error
* `test2(); // no args provided
* `test2(,); // insufficient args provided
*
* // arg1 is used as a var in an if statement,
* // This will compile fail because the macro
* // will expand to *if ( == "orange")*
* `test(, blue, pink)
*/
end
endprogram: test
CONSOLE OUTPUT # 1 Color # 3 Colors blue, , green # 1 Color black # 3 Colors blue, pink, green # 3 Colors gray, mistygreen, babypink # Oooo .. I received an Orange! # program-block >> macros2.sv, 31: this is a debug message # program-block >> macros2.sv, 32: this is a debug message
Following a consistent naming style for macros across your team is really useful. Since UVM is now widely used as the methodology for verfication, we could just borrow a page from their playbook and use the same coding style they use for macros. If you look at the UVM library source code, this is what you'll see:
function
or a task
use UPPERCASE macro names and lowercase argument namesclasses
, code-snippets
, etc - use lowecase macro names and UPPERCASE for argumentFor the next section I've picked out a few macros from the UVM library that show the above naming convention.
Sometimes all you need is a bunch of examples to look at to refresh your memory on how to use macros .. so here are some that I picked from the UVM library source code. You should be able to understand all of it from what you've learnt above.
//> src/base/uvm_phase.svh
`define UVM_PH_TRACE(ID,MSG,PH,VERB) \
`uvm_info(ID, {$sformatf("Phase '%0s' (id=%0d) ", \
PH.get_full_name(), PH.get_inst_id()),MSG}, VERB);
//> src/macros/uvm_tlm_defines.svh
// Check out the MacroStyleGuide in use here
// [ 1.] If the Macro defines a function or a task,
// use UPPERCASE for macro name and lower case for args
`define UVM_BLOCKING_TRANSPORT_IMP_SFX(SFX, imp, REQ, RSP, req_arg, rsp_arg) \
task transport( input REQ req_arg, output RSP rsp_arg); \
imp.transport``SFX(req_arg, rsp_arg); \
endtask
// [ 2a.] For everything else (i.e., if Macro defines
// a snippet of code, class or a convenience definition),
// use lowercase with UPPERCASE args
`define uvm_analysis_imp_decl(SFX) \
class uvm_analysis_imp``SFX #(type T=int, type IMP=int) \
extends uvm_port_base #(uvm_tlm_if_base #(T,T)); \
`UVM_IMP_COMMON(`UVM_TLM_ANALYSIS_MASK,`"uvm_analysis_imp``SFX`",IMP) \
function void write( input T t); \
m_imp.write``SFX( t); \
endfunction \
\
endclass
// [ 2b.] Examples of lowercase+uppercase with snippets
`define uvm_error(ID, MSG) \
begin \
if (uvm_report_enabled(UVM_NONE,UVM_ERROR,ID)) \
uvm_report_error (ID, MSG, UVM_NONE, `uvm_file, `uvm_line, "", 1); \
end// [ 3.] Separate words with underscores, don't use camelCase
`define uvm_non_blocking_transport_imp_decl(SFX) \
`uvm_nonblocking_transport_imp_decl(SFX)
//> src/macros/uvm_sequence_defines.svh
`define uvm_do(SEQ_OR_ITEM) \
`uvm_do_on_pri_with(SEQ_OR_ITEM, m_sequencer, -1, {})
`define uvm_do_with(SEQ_OR_ITEM, CONSTRAINTS) \
`uvm_do_on_pri_with(SEQ_OR_ITEM, m_sequencer, -1, CONSTRAINTS)
`define uvm_do_on_pri_with(SEQ_OR_ITEM, SEQR, PRIORITY, CONSTRAINTS) \
begin \
uvm_sequence_base __seq; \
`uvm_create_on(SEQ_OR_ITEM, SEQR) \
if (!$cast(__seq,SEQ_OR_ITEM)) start_item(SEQ_OR_ITEM, PRIORITY);\
if ((__seq == null || !__seq.do_not_randomize) && !SEQ_OR_ITEM.randomize() with CONSTRAINTS ) begin \
`uvm_warning("RNDFLD", "Randomization failed in uvm_do_with action") \
end\
if (!$cast(__seq,SEQ_OR_ITEM)) finish_item(SEQ_OR_ITEM, PRIORITY); \
else __seq.start(SEQR, this, PRIORITY, 0); \
end
`define uvm_create_on(SEQ_OR_ITEM, SEQR) \
begin \
uvm_object_wrapper w_; \
w_ = SEQ_OR_ITEM.get_type(); \
$cast(SEQ_OR_ITEM , create_item(w_, SEQR, `"SEQ_OR_ITEM`"));\
end
UPPERCASE
macro names and lowercase
args for functions and taskslowercase
macro names and UPPERCASE
args for everything else (like, classes, code-snippets, etc)`", ``
and `\
do