How to evaluate a VHDL expression
From Verific Design Automation FAQ
>>> This page is under construction <<<
Unlike in Verilog, expression evaluation in VHDL is fairly involved and needs all of the following:
- Either static elaborated tree or context from correct HierTreeNode pushed into the parse tree.
- VhdlNode::_present_scope set to the container scope where the expression is defined or to be used.
- Expression evaluation method set to static elaboration with VhdlNode::SetStaticElab().
- Constant pwr/gnd/x/z nets set with VhdlNode::SetConstNets(). Constant nets also need to be deleted to avoid memory leaks, but should only be done after the evaluated value (that is using those nets) is deleted.
The application examples below will illustrate the process.
C++ example 1 (via static elaboration):
#include "vhdl_file.h"
#include "VhdlUnits.h"
#include "VhdlIdDef.h"
#include "VhdlExpression.h"
#include "VhdlValue_Elab.h"
#include "VhdlDataFlow_Elab.h"
#include "Strings.h"
#include "Map.h"
#ifdef VERIFIC_NAMESPACE
using namespace Verific ;
#endif
// Restore back to the previous setting:
#define RETURN(N) { \
if (!static_elab) VhdlNode::SetRtlElab() ; \
VhdlNode::_present_scope = save_scope ; \
VhdlNode::ResetConstNets() ; \
return N ; \
}
int main(int argc, const char **argv)
{
const char *file_name = (argc > 1) ? argv[1] : "test.vhd" ;
const char *entity_name = (argc > 2) ? argv[2] : "test_entity" ;
const char *arch_name = (argc > 2) ? argv[2] : "test_arch" ;
const char *expr_string = "a(b) + c" ;
const char *lib_name = "work" ;
if (!vhdl_file::SetDefaultLibraryPath("../vdbs")) return 1 ;
if (!vhdl_file::Analyze(file_name)) return 2 ;
// Step (0): Need to have the parse tree static elaborated already:
if (!vhdl_file::Elaborate(entity_name, lib_name, arch_name, 0 /* generics */, 1 /* static elab */)) return 3 ;
const VhdlLibrary *work = vhdl_file::GetLibrary(lib_name) ;
if (!work) return 4 ;
const VhdlPrimaryUnit *ent = work->GetPrimUnit(entity_name) ;
if (!ent) return 5 ;
const VhdlSecondaryUnit *arch = ent->GetSecondaryUnit(arch_name) ;
if (!arch) return 6 ;
VhdlScope *arch_scope = arch->LocalScope() ;
if (!arch_scope) return 7 ;
// Step (1): Need to set _present_scope:
VhdlScope *save_scope = VhdlNode::_present_scope ;
VhdlNode::_present_scope = arch_scope ;
// Step (2): Need to set constant _pwr, _gnd, _x, _z nets:
VhdlNode::SetConstNets() ;
// Step (3): Need to set evaluation flow to use static elaboration:
unsigned static_elab = VhdlNode::IsStaticElab() ;
if (!static_elab) VhdlNode::SetStaticElab() ;
// Apply following known values to evaluate "a(b) + c":
// a = (2, 3)
// b = 0
// c = 1
Map known_values(STRING_HASH) ;
(void) known_values.Insert("a", "(2, 3)") ;
(void) known_values.Insert("b", "0") ;
(void) known_values.Insert("c", "1") ;
// Create a data-flow and set it to be in the "initial" phase
// and in subprograms, so that static elab uses the values from there:
VhdlDataFlow df(0) ;
df.SetInInitial() ;
df.SetInSubprogramOrProcess() ;
// The known values need to be in the condition-id-value Map to be picked up:
Map *id_value_map = new Map(POINTER_HASH, known_values.Size()) ;
df.SetConditionIdValueMap(id_value_map) ;
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:
VhdlIdDef *id = arch->FindDeclared(id_name) ;
if (!id) RETURN(8) ;
// Create the expression:
VhdlExpression *expr = vhdl_file::AnalyzeExpr(val_str) ;
if (!expr) RETURN(9) ;
// Resolve the expression so that the id-refs are resolved:
(void) expr->TypeInfer(id->Type() /* expected type */, 0 /* return types */, 0 /* environment */, arch_scope) ;
// Evaluate the expression:
// If this is a literal expression, we do not need the value value or Resolve() call or else we need both:
VhdlConstraint *constraint = id->Constraint() ;
VhdlValue *val = expr->Evaluate(constraint, 0 /* DataFlow */, 0) ;
delete expr ;
if (!val) RETURN(10) ;
// Insert into the table to be used later:
//df.SetAssignValue(id, val) ; // This only works for variables and not for signals
(void) id_value_map->Insert(id, val) ;
}
// Now create the expression for to be evaluated:
VhdlExpression *expr = vhdl_file::AnalyzeExpr(expr_string) ;
if (!expr) RETURN(11) ;
// Resolve the expression (in self-context) so that the id-refs are resolved:
// NOTE: May need to have the context (target) type for resolving context dependent expressions:
VhdlIdDef *target_type = expr->TypeInfer(0 /* expected type */, 0 /* return types */, 0 /* environment */, arch_scope) ;
if (!target_type) RETURN(12) ;
// Evaluate the expression with the proper target type:
VhdlValue *val = expr->Evaluate(target_type->Constraint(), &df /* use this table with the known values */, 0) ;
delete expr ;
if (!val) RETURN(13) ; // Failed to evaluate
// Show the result:
char *image = val->Image() ;
verific_int64 result = val->Integer() ;
arch->Info("Evaluated value: %s (%d)", ((image)?image:""), result) ;
Strings::free(image) ;
// Can create constant expression node so that the value can be deleted and ResetConstNets() can safely be called:
expr = val->CreateConstantVhdlExpression(0, target_type, 0) ;
delete val ;
if (!expr) RETURN(14) ;
// Show the expression node:
image = expr->GetPrettyPrintedString() ;
arch->Info("Converted expression: %s", image) ;
Strings::free(image) ;
delete expr ;
RETURN(0) ;
}
VHDL testcase:
library ieee ;
use ieee.std_logic_1164.all ;
entity test_entity is
end ;
architecture test_arch of test_entity is
type int_array is array (1 downto 0) of integer ;
signal a : int_array ;
signal b : integer ;
signal c : integer ;
begin
end ;
Run:
$ test-linux INFO: default VHDL library search path is now "/mnt/awing5_customers/Verific/extra_tests/vdbs" (VHDL-1504) -- Analyzing VHDL file 'test.vhd' (VHDL-1481) -- Restoring VHDL unit 'ieee.std_logic_1164' from file '/mnt/awing5_customers/Verific/extra_tests/vdbs/ieee/std_logic_1164.vdb' (VHDL-1493) -- Restoring VHDL unit 'std.standard' from file '/mnt/awing5_customers/Verific/extra_tests/vdbs/std/standard.vdb' (VHDL-1493) test.vhd(3): INFO: analyzing entity 'test_entity' (VHDL-1012) test.vhd(6): INFO: analyzing architecture 'test_arch' (VHDL-1010) test.vhd(3): INFO: processing 'test_entity(test_arch)' (VHDL-1067) test.vhd(12): INFO: Evaluated value: 4 (4) test.vhd(12): INFO: Converted expression: 4 $
C++ example 2 (via hierarchy tree creation):
#include "vhdl_file.h"
#include "vhdl_tokens.h"
#include "VhdlExpression.h"
#include "VhdlValue_Elab.h"
#include "hier_tree.h"
#include "HierTreeNode.h"
#ifdef VERIFIC_NAMESPACE
using namespace Verific ;
#endif
int main(void)
{
vhdl_file::SetDefaultLibraryPath("../vdbs") ;
const char *file_name = "design.vhdl" ;
if (!vhdl_file::Analyze(file_name)) return 1 ;
VhdlLibrary *work = vhdl_file::GetLibrary("work") ;
if (!work) return 2 ;
Array vhdl_libs ;
vhdl_libs.Insert(work) ;
const Map *top_nodes = hier_tree::CreateHierarchicalTreeAll(0, &vhdl_libs, 0) ;
if (!top_nodes) return 3 ;
HierTreeNode *test = (HierTreeNode *)top_nodes->GetValue("test") ;
if (!test) return 4 ;
HierTreeNode *u1 = test->GetChild("u1") ;
if (!u1) return 5 ;
Map *context = u1->PushContext() ;
//VhdlScope *node_scope = u1->GetNodeScope() ; // Entity scope
VhdlScope *node_scope = u1->GetVhdlArchitectureScope() ; // Architecture scope
if (!node_scope) { u1->PopContext(context) ; return 6 ; }
VhdlScope *save = VhdlNode::_present_scope ;
unsigned stat_elab = VhdlNode::IsStaticElab() ;
VhdlNode::_present_scope = node_scope ;
VhdlNode::SetConstNets() ;
VhdlNode::SetStaticElab() ;
// If you have the expression, use that; or parse one:
VhdlExpression *expr = vhdl_file::AnalyzeExpr("A + B") ;
if (!expr) { u1->PopContext(context) ; return 7 ; }
(void) expr->TypeInfer(0, 0, VHDL_READ, node_scope) ;
VhdlValue *val = expr->Evaluate(0, 0, 0) ;
if (!val) { delete expr ; u1->PopContext(context) ; return 8 ; }
char *img = val->Image() ;
char *pp_str = expr->GetPrettyPrintedString() ;
expr->Info("Expression '%s' evaluated to '%s'", pp_str, img) ;
Strings::free(pp_str) ;
Strings::free(img) ;
delete val ;
delete expr ;
VhdlNode::ResetConstNets() ;
if (!stat_elab) VhdlNode::SetRtlElab() ;
VhdlNode::_present_scope = save ;
u1->PopContext(context) ;
return 0 ;
}
VHDL testcase:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
entity mult is
generic (
A: integer :=0;
B: integer :=7 );
port (ain : in integer range (A) to (B);
z : out integer range (A) to (B));
end mult;
architecture xxx of mult is
begin
process(ain) begin
z <= ain ;
end process;
end xxx;
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
entity test is
port (ain1 : in integer range (0) to (2);
cout1: out integer range (0) to (2));
end test;
architecture rtl of test is
component mult
generic (
A: integer :=0;
B: integer :=2 );
port (ain : in integer range (A+B) to (B);
z : out integer range (A) to (B) );
end component;
begin
u1: mult
generic map(
A=> 0,
B=> 1
)
port map(ain=>ain1,z=>cout1);
end rtl;
Run:
$ test-linux INFO: default VHDL library search path is now "/mnt/awing5_customers/Verific/extra_tests/vdbs" (VHDL-1504) -- Analyzing VHDL file 'design.vhdl' (VHDL-1481) -- Restoring VHDL unit 'ieee.std_logic_1164' from file '/mnt/awing5_customers/Verific/extra_tests/vdbs/ieee/std_logic_1164.vdb' (VHDL-1493) -- Restoring VHDL unit 'std.standard' from file '/mnt/awing5_customers/Verific/extra_tests/vdbs/std/standard.vdb' (VHDL-1493) -- Restoring VHDL unit 'ieee.std_logic_arith' from file '/mnt/awing5_customers/Verific/extra_tests/vdbs/ieee/std_logic_arith.vdb' (VHDL-1493) design.vhdl(5): INFO: analyzing entity 'mult' (VHDL-1012) design.vhdl(14): INFO: analyzing architecture 'xxx' (VHDL-1010) design.vhdl(25): INFO: analyzing entity 'test' (VHDL-1012) design.vhdl(30): INFO: analyzing architecture 'rtl' (VHDL-1010) INFO: Expression '(a + b)' evaluated to '1' $