How to evaluate a Verilog expression
Evaluating a Verilog expression requires 'Static Elaboration' or 'Hierarchy Tree' feature.
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) $
If an element in the expression crosses module boundaries, elaboration needs to be run as in the example below:
Verilog testcase:
interface ifc #(int N=10);
logic [N-1:0] data;
endinterface
module test();
ifc #(.N(13)) I();
int my_int = $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' $
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:
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 $