Value of UVMLint - an anecdote from Race Conditions in UVM BCL code

Race conditions are one of the dreaded aspects of Verilog/SystemVerilog. Race conditions in SystemVerilog  lead to unpredictable behavior due to competing threads or processes accessing shared resources without proper synchronization. Debugging race conditions is challenging due to their non-deterministic nature, which means issues may appear sporadically and be hard to reproduce, complicating the identification and resolution of the underlying problem. It gets compounded by the fact that different EDA tools can behave differently on your code (with race condition) and yet be 100% LRM compliant (meaning no one will fix it for you). 

Would you believe that up until recently, UVM Base Class Library (BCL) had potential race conditions deep inside? Hard to believe? See for yourself a report from IEEE 1800.2 UVM core team members: 08376: 'static const xx ' class members with initialization - Accellera Mantis (mantishub.io)


Understanding Race Conditions in UVM: Why Switching from static const to localparam Matters

 The problem stems from how static const variables are initialized in SystemVerilog. If you're working with UVM or SystemVerilog, it's crucial to understand how variable initialization timing can impact simulation behavior—especially when using static const.

The Issue: Timing of Initialization

In SystemVerilog, static class members are shared across all instances and are typically initialized at runtime (LRM 1800-2017, §8.8.2). However, the exact timing of this initialization isn't guaranteed (LRM §6.24). For example, if you declare a static const string and try to use it early in your simulation, there's a risk that it hasn't been initialized yet. This is what we call a race condition: the code runs, but the initialization hasn't finished, leading to undefined or incorrect behavior.

The Fix: Using localparam

A better solution in this case is to use localparam instead of static const. Why? Because localparam variables are initialized at elaboration time (LRM §6.20), before the simulation even starts. This guarantees that the variable is ready and initialized when your simulation begins, avoiding any race conditions.

In SystemVerilog, localparam inside a class is not initialized at runtime, unlike static class members. Even when declared inside a class, localparam is initialized during elaboration, which happens before the simulation starts. This is an important distinction from static members, which are initialized at runtime. Below is an extract from the LRM on const:

So, in a nutshell:

  • localparam in a class: Initialized at elaboration, even if declared inside a class.
  • static const in a class: Initialized during runtime, when the class is first used or loaded into memory.
  • Using UVMLint to flag such issues:

    Given the hard work put in by the IEEE 1800.2 core team members to spot such issues manually earlier, our team at AsFigo got together and explored adding custom lint rules to identify any such issues across code base. This is crucial due to 2 key reasons:
    • Are there more such code segments within IEEE UVM BCL?
    • How do i prevent such code from creeping in going forward?

    This is where UVMLint comes in very handy - especially custom lint (or as we would like to call it -BYOL: Build Your Own Linter) - we used PySlint as framework and built rules for this and other issues and our findings are below:


    We are humbled to have been able to find this automatically and serve the industry that largely uses UVM on daily basis. We would be thrilled to see how our team can build similar custom linters for you, contact us via https://www.asfigo.com today! 
     


    Comments

    Popular posts from this blog

    Don't go "wild" with Associative arrays in SystemVerilog - PySlint it!

    Porting a complete UVC to Verilator + UVM - an anecdote!

    Opensource testbench generator for VHDL designs, OSVVM included!