How to evaluate a Verilog expression
Evaluating a Verilog expression requires 'Static Elaboration' or 'Hierarchy Tree' feature.
Contents
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 $