Lexing The VHDL ' (tick) Token - vhdl

In VHDL it the ' character can be used to encapsulate a character token ie '.' or it can as an attribute separator (similarish to CPP's :: token) ie string'("hello").
The issue comes up when parsing an attribute name containing a character ie string'('a','b','c'). In this case a naive lexer will incorrectly tokenize the first '(' as a character, and all of the following actual character will be messed up.
There is a thread in comp.lang.vhdl google group from 2007 which asks a similar question
Titled "Lexing the ' char" that has an answer by user diogratia
case '\'': /* IR1045 check */
if ( last_token == DELIM_RIGHT_PAREN ||
last_token == DELIM_RIGHT_BRACKET ||
last_token == KEYWD_ALL ||
last_token == IDENTIFIER_TOKEN ||
last_token == STR_LIT_TOKEN ||
last_token == CHAR_LIT_TOKEN || ! (buff_ptr<BUFSIZ-2) )
token_flag = DELIM_APOSTROPHE;
else if (is_graphic_char(NEXT_CHAR) &&
line_buff[buff_ptr+2] == '\'') { CHARACTER_LITERAL:
buff_ptr+= 3; /* lead,trailing \' and char */
last_token = CHAR_LIT_TOKEN;
token_strlen = 3;
return (last_token);
}
else token_flag = DELIM_APOSTROPHE;
break;
See Issue Report IR1045:
http://www.eda-twiki.org/isac/IRs-VHDL-93/IR1045.txt
As you can see from the above code fragment, the last token can be
captured and used to di"sambiguate something like:
foo <= std_logic_vector'('a','b','c');
without a large look ahead or backtracking.
However, As far as I know, flex doesn't track the last token that was parsed.
Without having to manually keep track of the last parsed token, is there a better way to accomplish this lexing task?
I am using IntelliJ GrammarKit if that helps.

The idea behind IR1045 is to be able to tell whether a single quote/apostrophe is part of a character literal or not without looking ahead or backtracking when you're wrong, try:
library ieee;
use ieee.std_logic_1164.all;
entity foo is
port (
a: in std_logic;
b: out std_logic_vector (3 downto 0)
);
end entity;
architecture behave of foo is
begin
b <= std_logic_vector'('0','1','1','0') when a = '1' else
(others =>'0') when a = '0' else
(others => 'X');
end architecture behave;
How far ahead are you willing to look?
There is however a practical example of flex disambiguation of apostrophes and character literals for VHDL.
Nick Gasson's nvc uses flex, in which he implemented an Issue Report 1045 solution.
See the nvc/src/lexer.l which is licensed under GPLv3.
Search for last_token:
#define TOKEN(t) return (last_token = (t))
and
#define TOKEN_LRM(t, lrm) \
if (standard() < lrm) { \
warn_at(&yylloc, "%s is a reserved word in VHDL-%s", \
yytext, standard_text(lrm)); \
return parse_id(yytext); \
} \
else \
return (last_token = (t));
An added function to check it:
static int resolve_ir1045(void);
static int last_token = -1;
which is:
%%
static int resolve_ir1045(void)
{
// See here for discussion:
// http://www.eda-stds.org/isac/IRs-VHDL-93/IR1045.txt
// The set of tokens that may precede a character literal is
// disjoint from that which may precede a single tick token.
switch (last_token) {
case tRSQUARE:
case tRPAREN:
case tALL:
case tID:
// Cannot be a character literal
return 0;
default:
return 1;
}
}
The IR1045 location has changed since the comp.lang.vhdl post it's now
http://www.eda-twiki.org/isac/IRs-VHDL-93/IR1045.txt
You'll also want to search for resolve_ir1045 in lexer.l.
static int resolve_ir1045(void);
and
{CHAR} { if (resolve_ir1045()) {
yylval.s = strdup(yytext);
TOKEN(tID);
Where we find nvc uses the function to filter detecting the first single quote of a character literal.
This was originally an Ada issue. IR-1045 was never adopted but universally used. There are probably Ada flex lexers that also demonstrate disambiguation.
The requirement to disambiguate is discussed in Ada User Journal volume 27 number 3 from September 2006 in an article Lexical Analysis on PDF pages 30 and 31 (Volume 27 pages 159 and 160) where we see the solution is not well known.
The comment that character literals do not precede a single quote is inaccurate:
entity ir1045 is
end entity;
architecture foo of ir1045 is
begin
THIS_PROCESS:
process
type twovalue is ('0', '1');
subtype string4 is string(1 to 4);
attribute a: string4;
attribute a of '1' : literal is "TRUE";
begin
assert THIS_PROCESS.'1''a /= "TRUE"
report "'1''a /= ""TRUE"" is FALSE";
report "This_PROCESS.'1''a'RIGHT = " &
integer'image(This_PROCESS.'1''a'RIGHT);
wait;
end process;
end architecture;
The first use of an attribute with selected name prefix that has a suffix that is a character literal demonstrates the inaccuracy, the second report statement shows it can matter:
ghdl -a ir1045.vhdl
ghdl -e ir1045
ghdl -r ir1045
ir1045.vhdl:13:9:#0ms:(assertion error): '1''a /= "TRUE" is FALSE
ir1045.vhdl:15:9:#0ms:(report note): This_PROCESS.'1''a'RIGHT = 4
In addition to an attribute name prefix containing a selected name with a character literal suffix there's a requirement that an attribute specification 'decorate' a declared entity (of an entity_class, see IEEE Std 1076-2008 7.2 Attribute specification) in the same declarative region the entity is declared in.
This example is syntactically and semantically valid VHDL. You could note that nvc doesn't allow decorating a named entity with the entity class literal. That's not according to 7.2.
Enumeration literals are declared in type declarations, here type twovalue. An enumerated type that has at least one character literal as an enumeration literal is a character type (5.2.2.1).

Related

vhdl: Why is aggregate assignment not allowed in this context?

I am trying to use aggregate assignments within a conditional assignment statement in the lines labelled "PROBLEMATIC LINE" in the following code implementation for a priority encoder module.
library ieee;
use ieee.std_logic_1164.all;
entity SN74LS148 is -- 8 to 3 line priority encoder module
port(EI : in std_logic; -- input enable
input : in std_logic_vector(0 to 7); -- 8 bit input bus
A : out std_logic_vector(2 downto 0); -- 3 output bits
GS, EO : out std_logic -- valid bit, enable output
);
end SN74LS148;
architecture behavioral of SN74LS148 is
signal truth_table : std_logic_vector(2 downto 0);
begin
truth_table <= "HHH" when input = (others => 'H') else -- PROBLEMATIC LINE
"LLL" when input(7) = 'L' else
"LLH" when input(6) = 'L' else
"LHL" when input(5) = 'L' else
"LHH" when input(4) = 'L' else
"HLL" when input(3) = 'L' else
"HLH" when input(2) = 'L' else
"HHL" when input(1) = 'L' else
"HHH" when input(0) = 'L' else
"XXX";
A <= truth_table when EI = 'L' else -- device enabled (active low)
"HHH" when EI = 'H' else -- device disabled (all outputs inactive)
"XXX";
GS <= 'H' when EI = 'H' -- invalid when device disabled
or input = (others => 'H') else -- or none of the lines asserted (PROBLEMATIC LINE)
'L';
EO <= 'L' when EI = 'L' and input = (others => 'H') else -- PROBLEMATIC LINE
'H';
end behavioral;
I am using the GHDL compiler. The error that I am getting is
encoder8x3.vhd:28:43: 'others' choice not allowed for an aggregate in this context
truth_table <= "HHH" when input = (others => 'H') else
^
encoder8x3.vhd:46:47: 'others' choice not allowed for an aggregate in this context
or input = (others => 'H') else -- or none of the lines asserted
^
encoder8x3.vhd:50:45: 'others' choice not allowed for an aggregate in this context
EO <= 'L' when EI = 'L' and input = (others => 'H') else
^
I guess I can fix this easily by hardcoding the inputs but what I want to know is why I am getting this error when the size of input has been specified in the port. This is not an ambiguity issue right ?
No, it is an ambiguity issue.
See IEEE Std 1076-1993 7.3.2.2 Array aggregates
The subtype of an array aggregate that has an others choice must be determinable from the context. That is, an array aggregate with an others choice may only appear
a. As an actual associated with a formal parameter or formal generic declared to be of a constrained array subtype (or subelement thereof)
...
Here the actual is your array aggregate associated with a parameter of the subprogram parameter for the overloaded equality operator, which is unconstrained. The type mark of that parameter would be the type std_logic_vector, while this would become a subtype in -2008 to allow subtype resolution specification it'd be unconstrained and the language of -2008 9.3.3.3 Array aggregates has been changed:
The index range of an array aggregate that has an others choice shall be determinable from the context.
That actually implies what's important here, the length of the aggregate array value. See 7.2.2 Relational operators:
Two scalar values of the same type are equal if and only if the values are the same. Two composite values of the same type are equal if and only if for each element of the left operand there is a matching element of the right operand and vice versa, and the values of matching elements are equal, as given by the predefined equality operator for the element type.
These rules also tell us the std_logic weak driving 'H' or 'L' enumeration values either as a scalar or an element of a composite value are not equal to the strong driving '1' and '0' values respectively.
As equality operator is defined by VHDL (7.2.2 of IEEE-1076-1993), there is no direct constraint between operands. For instance, if both operands are of the same array type, they may be not the same length (and equality will be always false), if they are of same length, elements are matched in order (whatever the range of array). This gives no strong constraint between operands.
In some contexts, literal array aggregates with others can be determined from context (initializations, assignments, etc.). Valid cases are listed in 7.3.2.2. Equality operator operands is not one of them.
You may still write an aggregate with an explicit range, for instance input = (input'range => 'H').

Error (10327) can't determine definition of operator ""=""

For this VHDL design description:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
entity four_bit_counter is
port (
count_out : out unsigned (3 downto 0);
clk : in std_logic;
enable:in std_logic;
reset: in std_logic) ;
end four_bit_counter;
architecture arc of four_bit_counter is
signal count : unsigned (3 downto 0);
begin
process (clk, reset, enable)
begin
if (reset = '1') then
count <= "0000";
elsif (clk' event and clk = '1') then
if (enable = '1') then
if (count="1010") then
count<="0000";
else
count <= count +1;
end if;
end ;
end if;
end process;
count_out <= count;
end arc;
I have the error
Error (10327): VHDL error at four_bit_counter.vhd(22): can't determine definition of operator ""="" -- found 2 possible definitions
and I don't know how to fix this.
The two possible definitions are both from package std_logic_arith
function "="(L: UNSIGNED; R: UNSIGNED) return BOOLEAN;
function "="(L: SIGNED; R: UNSIGNED) return BOOLEAN;
The ambiguity comes from overload resolution for the equality operator due to the package providing the second definition and how the type of the right operand a string literal is determined.
See IEEE Std 1076-2008 9.3.2 Literals:
String and bit string literals are representations of one-dimensional arrays of characters. The type of a string or bit string literal shall be determinable solely from the context in which the literal appears, excluding the literal itself but using the fact that the type of the literal shall be a one-dimensional array of a character type. The lexical structure of string and bit string literals is defined in Clause 15.
Here the context is the equality operator, where 12.5 The context of overload resolution specifies the error:
Overloading is defined for names, subprograms, and enumeration literals.
For overloaded entities, overload resolution determines the actual meaning that an occurrence of an identifier or a character literal has whenever the visibility rules have determined that more than one meaning is acceptable at the place of this occurrence; overload resolution likewise determines the actual meaning of an occurrence of an operator or basic operation (see 5.1).
At such a place, all visible declarations are considered. The occurrence is only legal if there is exactly one interpretation of each constituent of the innermost complete context. ...
Overloads for operators are provided by function declarations (
4.5.2 Operator overloading). For a binary operator the left operand is association with the first function parameter and the right parameter is associated with the second parameter. The return value is a Boolean (again from context, it's an if statement's condition which is a Boolean expression).
The type of the right operand can't be determined from context.
There are several possible solutions
Use the IEEE's numeric_std package instead where functions implementing operators don't have both signed and unsigned operands. Package numeric_std is incorporated into the standard's -2008 revision leading to package std_logic_arith's eventual deprecation.
Use a qualified expression (9.3.5) to specifically state the type of the operand.
if count = unsigned'("1010") then
(the redundant parentheses around the condition have been removed for clarity)
Rely on an abstract literal (15.5) where the ambiguity doesn't exist. This can either be of the form of a decimal literal (15.5.2) or based literal (15.5.3).
Use a function call to the overloaded operator's declaration with a selected name noting that named association can't be used for operator overloads.
Define an object (here a constant) with that value provided by a value expression. The type is inherent in an object declaration.

Condition: Logical state of multi-bit packed array

The configuration below
reg [3:0] myreg;
always#(...) begin
...
if(myreg) begin <events> end ...
end
How reference to "myreg" in condition without referring specific bit and logical operation between bits in packed array will be handled?
Edit: Would you please provide where this behavior you explained is documented? As this semantic structure is allowed, I guess there should be some documentation about it... Thank you!
Edit 1: Thanks for all your answers so far. Let's refocus on the following two points:
Why you believe logical value of (myreg) is reduction OR (not AND for example) - is there standard guideline? Let's turn off common sense and our feeling of "right" and "wrong" - these are not the matters we can base our conclusions on in mathematics/cybernetics.
is it true that "it may behave differently with another simulator" - or in the real life?
This strikes me because (a) I can not find document which states that it MUST be a reduction OR, and (b) that Quartus (which I use) does not give any warning or error for the syntax above.
Yes, #Morgan is correct Xillinx ISE and Questasim is doing same, When I synthesis the following code.
module aa( input clk,
input rst_n,
input [4:0] my_reg,
output reg chk
);
always # (posedge clk or negedge rst_n)
begin
if (!rst_n) begin
chk <= 1'bz;
end else begin
if (my_reg) begin
chk <= 1'b1;
end
else begin
chk <= 1'b0;
end
end
end
endmodule
You can see here schematic view,
Verilog should behave like this:
myreg | result | comment
------|--------|--------
0000 | 1'b0 | FALSE - all bit bits are zero
0010 | 1'b1 | TRUE - at least one bit is one
001X | 1'b1 | TRUE - at least one bit is one
000X | 1'bX | FALSE - we don't know whether all bits are zero or not
and if interprets 1'bX as FALSE
From this EDA Playground example VCS 2014.2 is interpreting :
if (myreg) begin
...
as an OR bit reduction of myreg ie:
if ( |myreg == 1'b1) begin
...
As Greg found the relevant (SystemVerilog IEEE 1800-2012 Standard) section of the LRM is Section 12.4
it evaluates to false (that is, has a zero value or the value is x or z),
The code supplied if (myreg) is valid but given x's or z's will evaluate to false rather than propagating x's you may find a difference between RTL and gate level simulations if you use un-initialised registers, the initial x's will evaluate to false rather than potentially being 1 and evaluating to true.
if(myreg) is the same as if(myreg != 0) according to IEEE Std 1800-2012 § 12.4 Conditional if–else statement
The conditional statement (or if–else statement) is used to make a
decision about whether a statement is executed. Formally, the syntax
is given in Syntax 12-2.
conditional_statement ::= // from A.6.6
[ unique_priority ] if ( cond_predicate ) statement_or_null
{ else if ( cond_predicate ) statement_or_null }
[ else statement_or_null ]
unique_priority ::= unique | unique0 | priority
cond_predicate ::= expression_or_cond_pattern { &&& expression_or_cond_pattern }
expression_or_cond_pattern ::= expression | cond_pattern
cond_pattern ::= expression matches pattern
Syntax 12-2—Syntax for if–else statement (excerpt from Annex A)
If the cond_predicate expression evaluates to true (that is, has a nonzero known value), the first statement shall be executed. If it evaluates to false (that is, has a zero value or the value is x or z), the first statement shall not execute. If there is an else statement and the cond_predicate expression is false, the else statement shall be executed. Because the numeric value of the if expression is tested for being zero, certain shortcuts are possible. For example, the following two statements
express the same logic:
if(expression)
if(expression != 0)
Note the LRM is checking if the expression is a nonzero value. It is not constraining the width or even considering if it is a signed type. Negative numbers would be be true, only zero is false.
Synthesizers may do bit-wise compression which is equivalent logic as comparing to zero.

verilog- building a small combinational circuit

I want to build a small combinational circuit(a few or's, 1 and, 1 not gates), and I stumbled upon a problem in the test bench(maybe even before) and was hoping that someone can help me.
the circuit is here
This is the code:
module hewi(input D3,D2,D1,D0,output A0,A1,V);
wire k,s; //outputs of the and and not gates
not N01(k,D2);
and An1(s,k,D1);
or Ou1(A0,D3,s);
or Ou2(A1,D3,D2);
or Oufinal(V,D3,D2,D1,D0);
endmodule
This is the test-bench code
module test_benche();
wire D3,D2,D1,D0,A0,A1,V;
hewi test(D3,D2,D1,D0,A0,A1,V);
initial
begin
D3 = 1`b0;
D2 = 1`b0;
D1 = 1`b0;
D0 = 1`b0;
#50;
D3=1`b1;
D2=1`b0;
D1=1`b1;
D0=1`b1;
end
endmodule
The problem I receive is that it can't check any one of these expressions:
Undefined macro exists as: 'b0'
"testbench.sv", 8: token is '`b0'
D3=1`b0;
^
What am I missing here?
The syntax error is because you are using backtick (`) instead of single quotes (')
All compiler directives are preceded by the (`) character. This
character is called grave accent (ASCII 0x60). It is different from
the character ('), which is the apostrophe character (ASCII 0x27). The
scope of a compiler directive extends from the point where it is
processed, across all files processed in the current compilation unit,
to the point where another compiler directive supersedes it or the
processing of the compilation unit completes. The semantics of
compiler directives is defined in LRM Section 3.12.1 and 5.6.4.
Regarding the following error :
Error-[IBLHS-NT] Illegal behavioral left hand side testbench.sv, 15 Net type cannot be used on the left side of this assignment. The offending expression is : D1 Source info: D1 = 1'b1
wires can not be assigned in always or initial blocks. Therefore They need to be converted to reg type.
//Control signals reg type
reg D3,D2,D1,D0;
//Outputs driven from DUT wires
wire A0,A1,V;
hewi test(D3,D2,D1,D0,A0,A1,V);
initial
begin
D3 = 1'b0;
D2 = 1'b0;
D1 = 1'b0;
D0 = 1'b0;
#50;
D3=1'b1;
D2=1'b0;
D1=1'b1;
D0=1'b1;
end
endmodule

How to get simulation warning when comparing std_logic with 'X'?

In order to catch more bugs in simulation, it is an advantage to get a warning
if std_logic with 'X' is used in = compare.
When using the ieee.std_logic_1164 package, the std_logic compare function
= does not warn about 'X' in either of the operands. So the ´if´ branch in
the code below is taken when sl_i is '1', but the else branch is taken
when sl_i is either of '0', 'X', etc.:
if sl_i = '1' then
sl_o <= '1';
else -- sl_i in "UX0ZWLH-"
sl_o <= '0';
end if;
So for example an 'X' or 'U', due to use of some unknown or uninitialized
value, is silently ignored during simulation. However, if a warning was given,
it would improve the possibility of finding bugs in simulation, like when the
ieee.numeric_std package warns about metavalue ('X' etc.) in compare with
=.
? Is there any standard VHDL way to get warning for metavalues in std_logic
at compare with = ?
One possibility, is to make a package where the implicit = for std_logic is
redefined, and then use this package with .all so the = is replaced. Such
package with may look like:
library ieee;
use ieee.std_logic_1164.std_logic;
package std_logic_warning is
function "="(l, r : std_logic) return boolean;
end package;
library ieee;
use ieee.std_logic_1164.is_x;
use ieee.std_logic_1164; -- For access to "=" using std_logic_1164."="
package body std_logic_warning is
function "="(l, r : std_logic) return boolean is
begin
assert not (is_x(l) or is_x(r))
report "std_logic_warning.""="": metavalue detected"
severity WARNING;
return std_logic_1164."="(l, r);
end function;
end package body;
? Will such a redefine be VHDL standard compliant, and is it likely to work
with the tools in general ?
As Jim Lewis points out with reference to ISAC
IR2058 the idea of
overriding the implicit = may not work in general for tool when using
VHDL-2003. VHDL-2008 should fix this.
Note that above question and suggestion has been edited based on David Koontz
previous comments.
Depending on which language revision you are using, you may experience portability problems. I suggest you avoid them by using VHDL-2008, which removes a number of issues.
Prior to VHDL-2008, some tools did not allow explicit operators to overload implicit operators. Prior to VHDL-2008, tools interpreted the reference to "ieee.std_logic_1164.std_logic" differently. See ISAC IR2058 for a discussion: http://www.eda.org/isac/IRs-VHDL-2002/IR2058.txt. Even with the implementation of IR2058, my interpretation is that is_x will not be included in your reference to std_logic as only overloaded operators are included and not all functions - so if that works it is probably not portable between tools.
Hence, I would use VHDL-2008 and the following code. I replaced the reference to ieee.std_logic_1164."=" with one to ieee.numeric_std.std_match, as it is not clear to me if you can still reference an implicit operator once an explicit operator is visible to replace it - even if it is legal, I would expect this to be a fringe case that may break tools (make sure to report the bugs). Use of std_match also has the benefit of correctly handling an 'L' or 'H'.
library ieee;
use ieee.std_logic_1164.all;
package std_logic_warning is
function "="(l, r : std_logic) return boolean;
end package;
package body std_logic_warning is
function "="(l, r : std_logic) return boolean is
begin
assert not (is_x(l) or is_x(r))
report "std_logic_warning.""="": metavalue detected"
severity WARNING;
return ieee.numeric_std.std_match(l, r);
end function;
end package body;
If you don't like the behavior of std_match, you can use std_match as a template to create the functionality, however, I don't recommend this as synthesis tools may not like it.
While you could use the modified "=", as a counter suggestion, there are two sources of X, external from other designs and internal from uninitialized registers. I don't worry about uninitialized registers as I am rigorous about my resets. Hence, at the core level, I may (depending on my confidence of others or the testbench), use the assert statement directly on core inputs.
assert not is_x(core_input_1)
report "meta value detected on core_input_1" severity ERROR ;
There are a couple things wrong with this scenario.
There isn't an explicit std_logic_1164."=" defined. Equality and inequality operators are implicitly declared for all types.
IEEE Std 1076-1993 Section 3 Types, Para 2:
...The set of operations of a type includes the explicitly declared
subprograms that have a parameter or result of the type. The remaining
operations of a type are the basic operations and the predefined
operators (see 7.2). These operations are each implicitly declared for
a given type declaration immediately after the type declaration and
before the next explicit declaration, if any.
The extended name should be ieee.std_logic_1164."=".
A relational operator in an expression is given as an operator symbol, so no extended name with left and right arguments. A function call to the equality operator function you've declared can be an extended name.
As long as the "=" operator being declared in the package declaration isn't used elsewhere in the package body you could use a context clause before the package body:
library ieee;
use ieee.std_logic_1164."=";
and
return l = r;
Also the equality operator is rigidly defined.
IEEE Std 1076-1993, 7.2.2 Relational operators, Para 4:
... Two scalar values of the same type are equal if and only if the values are the same.
So, no equality returns FALSE just because they are meta values. It's illegal to redefine what 'equal' means.
And ya, I know of one tool that will let you analyze this or something close, but it'll fail when using the operator. Even if it worked it would not be portable (while still being illegal, immoral and likely bad for the waistline).
This code crashes in ghdl, it was an attempt to avoid using the -fexplicit flag artificially changing the precedence of overloaded operators, which isn't -2008 standard compliant:
library ieee;
use ieee.std_logic_1164.std_logic;
use ieee.std_logic_1164.to_x01;
use ieee.std_logic_1164.is_x;
package std_logic_warning is
function "="(l, r : std_logic) return boolean;
end package;
package body std_logic_warning is
use ieee.std_logic_1164."=";
function "="(l, r : std_logic) return boolean is
begin
if is_x(l) or is_x(r) then
report "std_logic_warning.""="": metavalue detected, returning FALSE"
severity WARNING;
return FALSE;
end if;
return l = r; -- std_logic_1164."="(l, r);
end function;
end package body;
library ieee;
use ieee.std_logic_1164.std_ulogic;
use ieee.std_logic_1164.std_logic;
-- use ieee.std_logic_1164.all;
use work.std_logic_warning.all;
entity warning_test is
end entity;
architecture foo of warning_test is
signal a: std_logic;
signal b: std_logic;
begin
UNLABELLED:
process
begin
wait for 1 ns;
a <= 'X';
wait for 1 ns;
b <= '1';
wait for 1 ns;
a <= '0';
wait for 1 ns;
b <= '0';
wait;
end process;
MONITOR:
process (a,b)
begin
assert a = b
report "a = b " & "( " & std_logic'image(a)
& "=" & std_logic'image(b) & " )"
severity NOTE;
end process;
end architecture;
ghdl -r warning_test
std_logic_warning.vhdl:17:7:#0ms:(report warning):
std_logic_warning."=": metavalue detected, returning FALSE
std_logic_warning.vhdl:57:9:#0ms:(assertion note): a = b ( 'U'='U' )
std_logic_warning.vhdl:17:7:#1ns:(report warning):
std_logic_warning."=": metavalue detected, returning FALSE
std_logic_warning.vhdl:57:9:#1ns:(assertion note): a = b ( 'X'='U' )
std_logic_warning.vhdl:17:7:#2ns:(report warning):
std_logic_warning."=": metavalue detected, returning FALSE
std_logic_warning.vhdl:57:9:#2ns:(assertion note): a = b ( 'X'='1' )
ghdl: exec error
I'd suspect the issue has something to do with stack allocation, determined by trying concurrent assertions instead of the monitor process (which output more before crashing).
What it does show before bombing out is that 'U'='U' is a valid equality comparison according to the LRM and your `"=" operator say it isn't.
You could note your equality operator "=" is declared in the package declaration while the use clause is in effect in the package body. There should be no IR2058 conflict noted by Jim in his answer. That use clause (in the package body) isn't visible to where your overload function is visible.
To use your declaration in the test bench the order of use clauses was carefully crafted:
library ieee;
use ieee.std_logic_1164.std_ulogic;
use ieee.std_logic_1164.std_logic;
-- use ieee.std_logic_1164.all;
use work.std_logic_warning.all;
This also side steps the use of the Modelsim or ghdl overload rule evasion (-fexplicit as a command line argument in ghdl).
Unfortunately this demonstrates how complex VHDL is more than anything else
An acquaintance pointed out there was recursion in the function call so I proved that was the case by changing the function name (and changing the assertion condition expression).
ghdl -a test.vhdl
ghdl -e warning_test
ghdl -r warning_test
test.vhdl:16:7:#0ms:(report warning): std_logic_warning."=": metavalue detected, returning FALSE
test.vhdl:56:9:#0ms:(assertion note): a = b ( 'U'='U' )
test.vhdl:16:7:#1ns:(report warning): std_logic_warning."=": metavalue detected, returning FALSE
test.vhdl:56:9:#1ns:(assertion note): a = b ( 'X'='U' )
test.vhdl:16:7:#2ns:(report warning): std_logic_warning."=": metavalue detected, returning FALSE
test.vhdl:56:9:#2ns:(assertion note): a = b ( 'X'='1' )
test.vhdl:56:9:#3ns:(assertion note): a = b ( '0'='1' )
The recursion revolves around the function return expression "=".
commenting out the use clause in the package body yields:
-- use ieee.std_logic_1164."=";
ghdl -a test.vhdl
test.vhdl:20:14: no function declarations for operator "="
ghdl: compilation error
Which says the use clause is necessary, and that's expected:
IEEE Std 1076-1993 Section 3 Types, Para 2:
...The set of operations of a type includes the explicitly declared
subprograms that have a parameter or result of the type. The remaining
operations of a type are the basic operations and the predefined
operators (see 7.2). These operations are each implicitly declared for
a given type declaration immediately after the type declaration and
before the next explicit declaration, if any.
So is the issue overload resolution? (No it's visibility).
10.5 The context of overload resolution:
Overloading is defined for names, subprograms, and enumeration
literals.
For overloaded entities, overload resolution determines the actual
meaning that an occurrence of an identifier or a character literal has
whenever the visibility rules have determined that more than one
meaning is acceptable at the place of this occurrence; overload
resolution likewise determines the actual meaning of an occurrence of
an operator or basic operation (see the introduction to Section 3).
We've first got to determine if both declarations of the function "=" are visible.
10.4 Use clauses, para 3:
Each selected name in a use clause identifies one or more declarations
that will potentially become directly visible. If the suffix of the
selected name is a simple name, character literal, or operator symbol,
then the selected name identifies only the declaration(s) of that
simple name, character literal, or operator symbol contained within
the package or library denoted by the prefix of the selected name.
10.4 Use clauses, para 4:
For each use clause, there is a certain region of text called the
scope of the use clause. This region starts immediately after the use
clause. If a use clause is a declarative item of some declarative
region, the scope of the clause extends to the end of the declarative
region. If a use clause occurs within the context clause of a design
unit, the scope of the use clause extends to the end of the
declarative region associated with the design unit. The scope of a use
clause may additionally extend into a configuration declaration(see
10.2 ).
And about here we note that this use clause is within the package body, and it's effect doesn't extend anywhere else. We also know that the declared function (originally also "=" is directly visible (i.e. not by selection) from the package declaration.
10.4 Use clauses, para 5:
In order to determine which declarations are made directly visible at
a given place by use clauses, consider the set of declarations
identified by all use clauses whose scopes enclose this place. Any
declaration in this set is a potentially visible declaration. A
potentially visible declaration is actually made directly visible
except in the following two cases:
a. A potentially visible declaration is not made directly visible if
the place considered is within the immediate scope of a homograph of
the declaration.
b. Potentially visible declarations that have the same designator are
not made directly visible unless each of them is either an enumeration
literal specification or the declaration of a subprogram (either by a
subprogram declaration or by an implicit declaration).
And There is a homograph, the function declaration in the package declaration.
From Appendix B Glossary:
B.117 homograph: A reflexive property of two declarations. Each of two declarations is said to be a homograph of the other if both declarations have the same identifier and overloading is allowed for at most one of the two. If overloading is allowed for both declarations, then each of the two is a homograph of the other if they have the same identifier, operator symbol, or character literal, as well as the same parameter and result type profile.(§ 1.3.1 , § 10.3 )
B.119 immediate scope: A property of a declaration with respect to the declarative region within which the declaration immediately occurs. The immediate scope of the declaration extends from the beginning of the declaration to the end of the declarative region. (§ 10.2 )
So the issue revolves around immediate scope in 10.4, a. And b tells us that if both are subprogram declarations they are both visible inside the subprogram body.
And we get down to the NOTES:
1--These rules guarantee that a declaration that is made directly
visible by a use clause cannot hide an otherwise directly visible
declaration.
The use clause declaration that would become potentially visible is with in the immediate scope of the subprogram declaration found in the package declaration.
And if it weren't:
10.5 The context of overload resolution, para 3:
At such a place, all visible declarations are considered. The
occurrence is only legal if there is exactly one interpretation of
each constituent of the innermost complete context; a complete context
is either a declaration, a specification, or a statement.
Not being legal is synonymous with error. We'd have gotten an error.
So the moral here is that VHDL can be complex especially when you're trying to game it. And what I was trying to do resulted in legal code that didn't do what was intended, and didn't generate and error until out of stack space at run time, a catch-22.
The displayed code can be made to function with the following change:
Remove the use clause in the package body:
-- use ieee.std_logic_1164."=";
Restore EquipDev's return statement:
return l = r; -- std_logic_1164."="(l, r);
becomes
return std_logic_1164."="(l, r);
again. (Undoing my error).
This gives:
ghdl -a std_logic_warning.vhdl
ghdl -e warning_test
ghdl -r warning_test
std_logic_warning.vhdl:17:7:#0ms:(report warning): std_logic_warning."=": metavalue detected, returning FALSE
std_logic_warning.vhdl:58:9:#0ms:(assertion note): a = b ( 'U'='U' )
std_logic_warning.vhdl:17:7:#1ns:(report warning): std_logic_warning."=": metavalue detected, returning FALSE
std_logic_warning.vhdl:58:9:#1ns:(assertion note): a = b ( 'X'='U' )
std_logic_warning.vhdl:17:7:#2ns:(report warning): std_logic_warning."=": metavalue detected, returning FALSE
std_logic_warning.vhdl:58:9:#2ns:(assertion note): a = b ( 'X'='1' )
std_logic_warning.vhdl:58:9:#3ns:(assertion note): a = b ( '0'='1' )
Which appears to be the correct result, because when b is assigned to '0' at 4 ns the assertion test won't trigger for either the function or monitor process.
Recap
Is it possible to replace the relational operator "=" for std_logic?
Yes, But it may not be practical. Notice that there are use clauses for all the package std_logic_1164 names used except "=". In -2008 you can declare contexts where you could construct such a context clause once and invoke it. Or otherwise you could fill the package declaration with use clauses.
And the entire point of this exercise was to show that the adopted definition of "=" wasn't valid for meta values and is not standard compliant. (The 7.2.2 para 4 quote above).
And as Jim points out assertion test monitors can take the place of any usefulness redefining "=" may have.

Resources