How to evaluate a Verilog expression

From Verific Design Automation FAQ
Revision as of 12:05, 18 November 2025 by Mohammad (Talk | contribs)

Jump to: navigation, search

Evaluating a Verilog expression requires 'Static Elaboration' or 'Hierarchy Tree' feature.


Evaluate an expression not in the design

In the example below, a, b, and c are declared in the Verilog module. There is no need to run static elaboration on the whole module. But 'Static Elaboration' feature is still required because the API VeriExpression::StaticEvaluate() is available only with 'Static Elaboration' feature.

C++:

#include "veri_file.h"
#include "VeriExpression.h"
#include "VeriModule.h"
#include "VeriBaseValue_Stat.h"

#include "Strings.h"
#include "Array.h"
#include "Map.h"

#ifdef VERIFIC_NAMESPACE
using namespace Verific ;
#endif

int main(int argc, const char **argv)
{
    const char *file_name = (argc > 1) ? argv[1] : "test.v" ;
    const char *module_name = (argc > 2) ? argv[2] : "test_module" ;

    Array files(1) ;
    files.Insert(file_name) ;
    if (!veri_file::AnalyzeMultipleFiles(&files, veri_file::SYSTEM_VERILOG)) return 1 ; 

    const VeriModule *mod = veri_file::GetModule(module_name) ;
    if (!mod) return 2 ; 

    // Apply following known values to evaluate "a[b] + c": 
    //   a = {1'b1, 1'b1}
    //   b = 2'b00
    //   c = 2'b01  

    const char *expr_string = "a[b] + c" ;
    Map known_values(STRING_HASH) ;
    (void) known_values.Insert("a", "{1'b1, 1'b1}") ;
    (void) known_values.Insert("b", "2'b00") ;
    (void) known_values.Insert("c", "2'b01") ;

    ValueTable df ;

    MapIter mi ;
    const char *id_name ;
    const char *val_str ;
    FOREACH_MAP_ITEM(&known_values, mi, &id_name, &val_str) {
        if (!id_name || !val_str) continue ;

        // Find the identifier:
        VeriIdDef *id = mod->FindDeclared(id_name) ;
        if (!id) return 3 ; 

        // Create the expression:
        VeriExpression *expr = veri_file::AnalyzeExpr(val_str, veri_file::SYSTEM_VERILOG) ;
        if (!expr) return 4 ;

        // Evaluate the expression:
        // If this is a literal expression, we do not need the value value or Resolve() call or else we need both:
        VeriBaseValue *val = expr->StaticEvaluate(0 /* self context */, 0 /* value table */) ;
        delete expr ;
        if (!val) return 5 ; 

        // Insert into the table to be used later:
        if (!df.Insert(id, val)) {
            delete val ;
            return 6 ;
        }
    }

    // Now create the expression for to be evaluated:
    VeriExpression *expr = veri_file::AnalyzeExpr(expr_string, veri_file::SYSTEM_VERILOG) ;
    if (!expr) return 7 ;

    // Resolve the expression so that the id-refs are resolved:
    expr->Resolve(mod->GetScope(), VeriTreeNode::VERI_UNDEF_ENV) ;

    VeriBaseValue *val = expr->StaticEvaluate(0 /* self context */, &df /* use this table with the known values */) ;
    delete expr ;
    if (!val) return 8 ; // Failed to evaluate

    char *image = val->Image() ;
    int result = val->GetIntegerValue() ;
    mod->Info("Evaluated value: %s (%d)", ((image)?image:""), result) ;
    Strings::free(image) ;
    delete val ;

    return 0 ;
}
 

Verilog testcase:

module test_module;
    wire a [1:0];
    wire [1:0] b;
    wire [1:0] c;
endmodule
 

Run:

$ test-linux 
-- Analyzing Verilog file 'test.v' (VERI-1482)
test.v(5): INFO: Evaluated value: 2'b10 (2)
$ 
 


A function call in the design and generate variables (genvar)

Function calls are similar to other expressions, and you can evaluate them using StaticEvaluate().

Giving a value to a generate variable is a little different. 'genvar' is treated like a parameter; we try to use the initial value instead of looking into the passed-in VeriValueTable. But since 'genvar' doesn't have a single value, we need to set it to a desired value before evaluating the expression.

In the example below, you can see how this can be done:

C++:

#include "veri_file.h"
#include "VeriModule.h"
#include "VeriVisitor.h"
#include "VeriExpression.h"
#include "VeriId.h"
#include "VeriBaseValue_Stat.h"
#include "Map.h"
#include "Strings.h"

#ifdef VERIFIC_NAMESPACE
using namespace Verific ;
#endif

class MyVisitor : public VeriVisitor
{
public:
    MyVisitor() : VeriVisitor() { }
    virtual ~MyVisitor() { }

    virtual void VERI_VISIT(VeriFunctionCall, node)
    {
        char *str = node.GetPrettyPrintedString() ;
        node.Info("Got function call: %s", str) ;

        ValueTable df ;
        Map old_param_val(POINTER_HASH) ;

        if (Strings::compare(str, "A(i)")) {

            // Apply following known values to evaluate: 
            //   i = 2 (or 2'b10)

            Map known_values(STRING_HASH) ;
            (void) known_values.Insert("i", "2'b10") ;

            unsigned i ;
            VeriExpression *expr ;
            // Going through arguments of the function call
            FOREACH_ARRAY_ITEM(node.GetArgs(), i, expr) {
                if (!expr) continue ;
                if (!expr->IsIdRef()) continue ;

                // Find the identifier:
                VeriIdDef *id = expr->GetId() ;
                if (!id) return ; 

                // Get the value that we want to set for this id
                const char *val_str = (const char *) known_values.GetValue(expr->GetName()) ;

                // Create the expression:
                VeriExpression *expr = veri_file::AnalyzeExpr(val_str, veri_file::SYSTEM_VERILOG) ;
                if (!expr) return ;

                if (id->IsParam()) {
                    // Param values are NOT taken from value-table, initial value is used:
                    old_param_val.Insert(id, id->TakeInitialValue()) ; // Store current value
                    (void) id->SetInitialValue(expr) ; // Absorbed
                    continue ;
                }

                // Evaluate the expression:
                // If this is a literal expression, we do not need the value value or Resolve() call or else we need both:
                VeriBaseValue *val = expr->StaticEvaluate(0 /* self context */, 0 /* value table */) ;
                delete expr ;
                if (!val) return ; 

                // Insert into the table to be used later:
                if (!df.Insert(id, val)) {
                    delete val ;
                    return ;
                }
            }
        }

        VeriBaseValue *val = node.StaticEvaluate(0 /* self context */, &df /* use this table with the known values */) ;

        MapIter mi ;
        VeriIdDef *id ;
        VeriExpression *expr ;
        FOREACH_MAP_ITEM(&old_param_val, mi, &id, &expr) {
            // Restore back old initial value:
            (void) id->SetInitialValue(expr) ; // Old value deleted
        }

        if (!val) {
            node.Info("Couldn't evaluate the expression"); 
            return ; // Failed to evaluate
        }

        char *image = val->Image() ;
        int result = val->GetIntegerValue() ;
        node.Info("Evaluated value: %s (%d)", ((image)?image:""), result) ;
        Strings::free(image) ;
        delete val ;
        Strings::free(str) ;
    }
} ;

int main (int argc, char **argv)
{
    const char *file_name = "test.v" ; // default input filename
    if (argc > 1) file_name = argv[1] ; // Set the file name as specified by the user

    // veri_file::Analyze(const char *file_name, unsigned verilog_mode=VERILOG_2K, const char *lib_name="work", unsigned cu_mode=NO_MODE) ;
    if (!veri_file::Analyze(file_name, veri_file::SYSTEM_VERILOG)) return 1 ;

    //if (!veri_file::ElaborateAllStatic()) return 1 ;

    MyVisitor mv ;

    MapIter mi ;
    VeriModule *mod ;
    FOREACH_VERILOG_MODULE(mi, mod) if (mod) mod->Accept(mv) ;

    return 0 ;
}
 

Verilog testcase:

module test();
    parameter d = 0;
endmodule

module top();  
    parameter PARAM = 5;
     
    function[3:0] A(input [2:0] c);  
        return (2 + c);  
    endfunction  
     
    test#(A(PARAM)) test_e();

    genvar i;
    for(i = 0; i < 10; i = i + 1) begin
        test#(A(i)) test_i();
    end
endmodule
 

Run:

$ ./test-linux 
-- Analyzing Verilog file 'test.v' (VERI-1482)
test.v(12): INFO: Got function call: A(PARAM)
test.v(12): INFO: Evaluated value: 4'b0111 (7)
test.v(16): INFO: Got function call: A(i)
test.v(16): INFO: Evaluated value: 4'b0100 (4)
$
 

An expression that crosses module boundaries

If an element in the expression to be evaluated crosses module boundaries, elaboration needs to be run as in the Verilog testcase below ('my_int' is to be evaluated):

interface ifc #(int N=10);
    logic [N-1:0] data;
endinterface
 
module test();
    ifc #(.N(13)) I();
    int my_int = $bits(I.data); // want to evaluated 'my_int' or '$bits(I.data)'
endmodule
 

The C++ example below calls static elaboration:

#include "veri_file.h"
#include "VeriModule.h"
#include "VeriVisitor.h"
#include "VeriExpression.h"

#ifdef VERIFIC_NAMESPACE
using namespace Verific ;
#endif

class MyVisitor : public VeriVisitor
{
public:
    MyVisitor() : VeriVisitor() { }
    virtual ~MyVisitor() { }

    virtual void VERI_VISIT(VeriExpression, node)
    {
        char *str = node.GetPrettyPrintedString() ;
        VeriExpression *new_exp = node.StaticEvaluateToExpr(0,0,0);
        if (new_exp) {
            char *str2 = new_exp->GetPrettyPrintedString() ;
            node.Info("    Expression: '%s', evaluated to '%s'", str, str2) ;
            Strings::free(str) ;
            Strings::free(str2) ;
        }
    }
} ;

int main (int argc, char **argv)
{
    const char *file_name = "test.sv" ; // defaulf input filename
    if (argc > 1) file_name = argv[1] ; // Set the file name as specified by the user
    if (!veri_file::Analyze(file_name, veri_file::SYSTEM_VERILOG)) return 1 ;

    if (!veri_file::ElaborateAllStatic()) return 1 ;

    MyVisitor mv ;

    VeriModule *mod = veri_file::GetModule("test") ;
    if (!mod) return 2 ;
    mod->Info("In module '%s'", mod->Name()) ;
    mod->Accept(mv) ;
    return 0 ;
}
 

Run:

$ test-linux 
-- Analyzing Verilog file 'test.sv' (VERI-1482)
test.sv(5): INFO: compiling module 'test' (VERI-1018)
test.sv(8): INFO: In module 'test'
test.sv(7): INFO:     Expression: 'int ', evaluated to 'int '
test.sv(7): INFO:     Expression: '$bits(I.data)', evaluated to '13'
$ 
 

Using Hierarchy Tree Elaboration to evaluate

If for some reason static elaboration is not desirable (it does take memory and CPU time), hierarchy-tree elaboration can be run instead. Hierarchy-tree elaboration takes much less memory anc CPU time than static elaborations does.

Reference: https://www.verific.com/docs/index.php?title=Hierarchy_Tree

The C++ example below uses hierarchy-tree elaboration instead of static elaboration:

C++:

#include "veri_file.h"
#include "VeriModule.h"
#include "VeriId.h"
#include "VeriExpression.h"
#include "VeriBaseValue_Stat.h"
#include "hier_tree.h"
#include "HierTreeNode.h"

#ifdef VERIFIC_NAMESPACE
using namespace Verific ;
#endif

int main(int argc, const char **argv)
{
    const char *file_name = "test.sv" ;
    const char *top_name = "test" ;
    const char *my_name = "my_int" ;

    if (!veri_file::Analyze(file_name, veri_file::SYSTEM_VERILOG)) return 1 ;

    VeriModule *top = veri_file::GetModule(top_name) ;
    if (!top) return 2 ;

    VeriIdDef *my_id = top->FindDeclared(my_name) ;
    if (!my_id) return 3 ;

    VeriExpression *init_val = my_id->GetInitialValue() ;
    if (!init_val) return 4 ;

    Array top_mods(1) ;
    top_mods.Insert(top) ;

    const Map *top_nodes = hier_tree::CreateHierarchicalTree(&top_mods, 0, 0, 0) ;
    if (!top_nodes) return 5 ;

    HierTreeNode *top_node = (HierTreeNode *)top_nodes->GetValue(top_name) ;
    if (!top_node) {
        hier_tree::DeleteHierarchicalTree() ;
        return 6 ;
    }

    InterfaceInfo *info = top_node->GetParamValue(my_id) ;
    VeriBaseValue *val = 0 ;
    VeriBaseValue *delete_val = 0 ;
    if (info) {
        // We already have the value evaluated, get from node:
        val = info->GetVeriEvaluatedValue() ;
    } else {
        // Need to evaluate the value, use the context from the node:
        Map *context = top_node->PushContext() ;
        hier_tree::PushNode(top_node) ;

        val = init_val->StaticEvaluate(0, 0, 0, 0) ;
        delete_val = val ;

        hier_tree::PopNode() ;
        top_node->PopContext(context) ;
    }

    char *val_img = (val) ? val->Image() : 0 ;
    init_val->Info("%s(%s) evaluated to %s", my_name, my_id->GetPrettyPrintedString(), ((val_img)?val_img:"<NULL>")) ;
    Strings::free(val_img) ;

    delete delete_val ;

    hier_tree::DeleteHierarchicalTree() ;

    return 0 ;
}
 

Run:

$ test-linux 
-- Analyzing Verilog file 'test.sv' (VERI-1482)
test.sv(7): INFO: my_int(my_int = $bits(I.data)) evaluated to 13
$