When connecting to the DUT signals, we usually refer to the values as 0s or 1s. But sometimes there is a need to know the exact multi-value logic, or to write an mvl value (e.g., init the signal to Z).
For this purpose, the e language defines the mvl type – a predefined enumerated type, with values of MVL_U, MVL_X, MVL_0, MVL_1, MVL_Z, MVL_W, MVL_L, MVL_H, and MVL_N (based on the VHDL std_logic type).
If you need to read these mvl values and you always have your eye on performance, this blog post is certainly for you.
Getting information of unknown bits
The following code creates a list of all mvl values in a port, and then applies lists’ methods for traversing the list, looking for U and X values.
update_port_info() is {
// “naive” code – creating lists and searching them
var mvlll := my_port.get_mvl_list();
me.is_initialized = !mvll.has(it == MVL_U);
me.has_unknown = mvll.has(it == MVL_X);
This simple code will work, but as the title suggests, it has a “price.” The price is that each call to get_mvl_list() creates a spare list variable.
It is more efficient to use the ports’ mvl predefined methods, which will get the required information without creating auxiliary lists.
update_port_info() is {
// TRUE is there is at least one bit that equals U
me.is_initialized = my_port.has_mvl_value(MVL_U);
// TRUE if there is at least one bit which is U, X, Z, W or don’t
// care (N)
me.has_unknown = my_port.has_unknown();
Efficient usage of MVL lists
While the suggestion above applies to some cases, there are unfortunately times that you cannot avoid creating lists of mvl values. For example, there are times when knowing whether there is an X value is simply not enough; rather, you must also know its location.
If you must create mvl lists frequently, consider using the new [1] Specman fill_mvl_list() predefined method. This method refills an existing list, rather than creating a new one.
Let’s examine the following code (before improving it):
It calls get_mvl_list() every cycle, which means that in each iteration, a new list of mvls is created and this will result in more frequent activations of garbage collection
checker()@clk is {
while TRUE {
// a new list is created and filled
var mvll := p.get_mvl_list();
for each in mvll {
case it {
[MVL_X,MVL_Z: { notify_unknown(index) }
[MVL_W,MVL_H,MVL_N]: { notify_weak(index) }
};
};
wait;
};
};
Let’s modify the code a bit to use the fill_mvl_list() method. This allows more aggressive dynamic memory optimization, which might be significant – especially when the checker method is called many times.
mvl_buffer : list of mvl;
checker()@clk is {
while TRUE {
// Update the content of local list,
// avoid creating a new list
p.fill_mvl_list(mvl_buffer);
for each in mvl_buffer {
case it {
[MVL_X,MVL_Z: { notify_unknown(index) }
[MVL_W,MVL_H,MVL_N]: { notify_weak(index) }
};
};
wait;
};
};
The code above defines a field, filling it with the list of mvl values, which means that many methods can access the list simultaneously. However, you must be careful here! You must ensure that there is no collision between methods reading and writing the list.
One rule can be: Only one method updates the list, and reading the list should be in zero time. For example:
mvl_buffer : list of mvl;
checker()@clk is {
p0.fill_mvl_list(mvl_buffer);
for each in mvl_buffer {
// Calling a non TCM, for checking the list values
do_the_check(it, index);
};
p1.put_mvl_list(mvl_buffer);
...
};
P.S. (our last tip before closing):
You could also use the get_mvl_string() predefined method which returns a string in which each character represents an mvl value (see Cadence Help for more information).
For detailed information on the mvltype and on predefined methods, see Cadence Help.