Skip to content

SystemVerilog Macros

Preview

  • Proper usage of macros makes life a lot easier. If you are unfamiliar with them and their assortment of symbols `, `", `\`" 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.
  • We will also explore some macros from the UVM source code and establish a style guide for writing macros
  • A cautionary note before we begin - Overuse of macros could lead to reduced readability of code ... so use them judiciously. Sometimes adding another layer of abstraction is not the best thing to do.

All code presented here can be downloaded from GitHub

Introduction

What are macros?

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.

Why should I use them?

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 -

  • You and your team could establish a library of macros
  • Use a naming convention for the macros in this library, such as <*>_utils (print_byte_utils, etc).
  • Put it in a file called macro_utils.sv and include it in your base package
  • Make it part of your Design/DV methodology to use these macros where applicable, instead of repeating code

Hope I've made a convincing case for Macros.


Subscribe

Get Notified when a new article is published!


Macro Syntax

Macro Name

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)

Backticks - ``, `", `\`"

There are 3 tokens that need some explanation

Backtick and Quotes

`" - 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

  1. Include the double quotes
  2. The arguments within the double-quotes should be substituted and
  3. Any embedded macros should be expanded.

Let's look at example 1.1

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.

Double Backtick

`` - (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

/* 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 - Space and period are natural token delimiters, i.e., the compiler can clearly identify the argument in the macro text. _2a uses a "period" between the argument MOD and the string master. _2b uses a "space" between argument MOD and string master.
  • `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.
  • In `append_front_2c_good the double-backtick `` 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.

Slashes, Ticks and Quotes

`\`" - 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

/* 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

Miscellaneous

  • You can call macros within macros
  • Using comments in the macro is allowed. There is nothing different about this.
  • Using ifdefs within the macro definition is also allowed, nothing different about this either

Passing Args

A few things to keep in mind about arguments to macros

  • Arguments can have default values
  • You can skip arguments by leaving that position empty, even if it doesn't have a default value. See what `test2(,,) prints out in example 2.
  • Check out `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

/* 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

Download the example code from this section

Macro Style Guide

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:

  1. If the macro defines a function or a task use UPPERCASE macro names and lowercase argument names
  2. If the macro defines anything like classes, code-snippets, etc - use lowecase macro names and UPPERCASE for argument
  3. Separate the words in the macro name with underscores

For the next section I've picked out a few macros from the UVM library that show the above naming convention.

Reference Examples

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

In a Nutshell

  • Follow a style coding style guide for consistency across the team
    • UPPERCASE macro names and lowercase args for functions and tasks
    • Separate words with underscore
    • lowercase macro names and UPPERCASE args for everything else (like, classes, code-snippets, etc)
    • Be judicious while using macros, overuse could make code unreadable
  • Know what `", `` and `\ do
  • Macros are pushed to the global namespace. Defining a macro within a class doesn't mean it is visible only to that class.
  • Watch out for macro re-definition warning in your compile logs
  • Use the examples in this article as reference when you write macros

Questions & Comments

For questions or comments on this article, please use this GitHub Discussions link.