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

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.

Related

VHDL Case choice is not locally static

This code works with some tools
Aldec Riviera Pro
but not others
GHDL ( ERROR choice must be locally static expression)
LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
USE IEEE.numeric_std.ALL;
ENTITY INSTRUCTION_PROCESSOR IS
PORT (
clk : IN std_logic;
instruction : IN INTEGER
);
END ENTITY INSTRUCTION_PROCESSOR;
ARCHITECTURE behavioural OF INSTRUCTION_PROCESSOR IS
TYPE INSTRUCTION_t IS RECORD
instr : INTEGER;
cycles : INTEGER;
END RECORD;
CONSTANT CMD_A : INSTRUCTION_t := (instr => 0, cycles => 5);
CONSTANT CMD_B : INSTRUCTION_t := (instr => 1, cycles => 3);
BEGIN
PROCESSOR : PROCESS (clk)
VARIABLE loop_cycles : INTEGER := 0;
BEGIN
IF clk'event AND clk = '1' THEN
CASE instruction IS
WHEN CMD_A.instr =>
loop_cycles := CMD_A.cycles;
WHEN CMD_B.instr =>
loop_cycles := CMD_B.cycles;
WHEN OTHERS =>
NULL;
END CASE;
END IF;
END PROCESS;
END ARCHITECTURE;
https://www.edaplayground.com/x/jYD
since CMD_A and CMD_B are declared as constant records i would expect this to work...
any words of wisdom or is it just a bad idea?
I'm not sure the ghdl-0.35 version on EDA playground is up to handling --std=08 (-2008) for this issue without trying it. A recent ghdl-0.37-dev version shows it works:
ghdl -a --std=08 instruction_processor.vhdl
ghdl -e --std=08 tb
instruction_processor.vhdl:68:8:error: for default port binding of component instance "uut":
instruction_processor.vhdl:68:8:error: type of signal interface "instruction" declared at line 56:9
instruction_processor.vhdl:68:8:error: not compatible with type of port "instruction" declared at line 9:9
instruction_processor.vhdl:68:8:error: signal interface "cycles" has no association in entity "instruction_processor"
ghdl:error: compilation error
even if the testbench and/or entity header needs a bit of work. Both INSTRUCTION_PROCESSOR and TB are located in the same design file used above.
The IEEE Std 1076-2008 revision changed some definitions in 9.4.2 Locally static primaries
9.4.2 Locally static primaries
An expression is said to be locally static if and only if every operator in the expression denotes an implicitly defined operator or an operator defined in one of the packages STD_LOGIC_1164, NUMERIC_BIT, NUMERIC_STD, NUMERIC_BIT_UNSIGNED, or NUMERIC_STD_UNSIGNED in library IEEE, and if every primary in the expression is a locally static primary, where a locally static primary is defined to be one of the following:
...
m) A record aggregate in which all expressions in element associations are locally static expressions.
...
Prior to -2008 a an aggregate could not be locally static. An aggregate is an expression which is 'a formula that defines the computation of a value', previously always globally static for a constant declaration value expression.
Allowing certain expressions to be locally static comes from the VHDL-200x effort producing the -2008 revision (Fast Track Proposal FT-22). The idea is expressions with locally static primaries that produce values from basic or predefined operations including those found in IEEE library packages listed above are implemented as pure functions and are not dependent on elaboration. To avoid confusion a procedure call is a statement.
Analyzing your code with Aldec Riviera Pro used a -2008 compliant flag according to the EDA playground session from your comment:
Should an earlier revision of the standard be required due to tool chain constraints either the case statement can be replaced by an if statement or a concurrent conditional assignment statement which implies and if statement equivalent. A selected signal assignment statement on the other hand implies a case statement and conforms to the same semantics.
Try to put constraint into the integer.
instruction : IN INTEGER RANGE 0 TO ...;

VHDL parse error, unexpected INTEGER_LITERAL, expecting RETURN or IDENTIFIER or RSQBRACK Process "Check Syntax" failed

I am writing a VHDL Program for 4:1 MUX and am facing a error on the line where process clause starts
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity mux_41 is
Port ( inp : in STD_LOGIC_VECTOR (0 downto 3);
outp : out STD_LOGIC;
sel : in STD_LOGIC_VECTOR (1 downto 0));
end mux_41;
architecture Behavioral of mux_41 is
begin
Process(inp[0],inp[1],inp[2],inp[3],sel[0],sel[1])
begin
if(sel = "00")
then outp <= inp[0];
elsif(sel = "01")
then outp <= inp[1];
elsif(sel = "10")
then outp <= inp[2];
else outp <= inp[3];
end if;
end process;
end Behavioral;
Is this the right way to pass sensitivity list to process?
In VHDL when you want to access to a particular element of an array (std_logic_vector are array), you have to use () instead of [].
Process(inp(0),inp(1),inp(2),inp(3),sel(0),sel(1))
begin
if(sel = "00")
then outp <= inp(0);
elsif(sel = "01")
then outp <= inp(1);
elsif(sel = "10")
then outp <= inp(2);
else outp <= inp(3);
end if;
end process;
The OP doesn't show the complete error message.
It appears to be in the sensitivity list for the process where inp is a signal_name and the particular parser doesn't use syntactic predicates:
IEEE Std 1076-2008 10.2 Wait statements
sensitivity_list ::= signal_name { , signal_name }
and here the selected names (8.3) are all static names (8.1) and all the elements of both sel and inp are evaluated in the process if statement (10.8) and should be included in the sensitivity set (10.2).
1.3.2 Syntactic description
The form of a VHDL description is described by means of context-free syntax using a simple variant of the Backus-Naur form (BNF); in particular:
...
g) If the name of any syntactic category starts with an italicized part, it is equivalent to the category name without the italicized part. The italicized part is intended to convey some semantic information. For example, type_name and subtype_name are both syntactically equivalent to name alone
...
It's possible to write a parser that uses a symbol table containing declarations of named entities (which precede the use of a name in a description). It simplifies both syntactical and semantic analysis while allowing the rules of visibility and scope to be tracked.
In any event not using syntactic predicates results in a different error message. Instead of an error describing a signal as not having a signature you get an error message saying the signature is malformed or isn't applicable (there are several rules that might be applicable without the syntactic predicates).
The error actually occurs for the first left square bracket. Selected names uses parentheses (round brackets in parts or Europe) instead of square brackets. Also inp by itself has elements 3 downto 0 or 0 to 3 (noting inp : in STD_LOGIC_VECTOR (0 downto 3) has a null range (5.2.1) and no elements).
5.2.1 pargraphs 3 and 4:
A range specifies a subset of values of a scalar type. A range is said to be a null range if the specified subset is empty.
The range L to R is called an ascending range; if L > R, then the range is a null range. The range L downto R is called a descending range; if L < R, then the range is a null range. ...
With a process sensitivity list a process statement (11.3) is assumed to have a single implicit wait statement as it's last sequential statement. The rule for constructing the sensitivity set for the implicit wait statement in a process statement are found in 10.2 Wait statement, where the implicit wait statement will use the longest static prefix (8.1) of any signal name in the process sensitivity list and would be equivalent to process (sel, inp).
11.3 Process statement paragraph 4:
If a process sensitivity list appears following the reserved word process, then the process statement is assumed to contain an implicit wait statement as the last statement of the process statement part; this implicit wait statement is of the form
wait on sensitivity_list ;
where the sensitivity list is determined in one of two ways. If the process sensitivity list is specified as a sensitivity list, then the sensitivity list of the wait statement is that following the reserved word process. ...
10.2 Wait statement paragraphs 3 and 4:
The sensitivity clause defines the sensitivity set of the wait statement, which is the set of signals to which the wait statement is sensitive. Each signal name in the sensitivity list identifies a given signal as a member of the sensitivity set. Each signal name in the sensitivity list shall be a static signal name, and each name shall denote a signal for which reading is permitted. If no sensitivity clause appears, the sensitivity set is constructed according to the following (recursive) rule:
The sensitivity set is initially empty. For each primary in the condition of the condition clause, if the primary is
— A simple name that denotes a signal, add the longest static prefix of the name to the sensitivity set.
— An expanded name that denotes a signal, add the longest static prefix of the name to the sensitivity set.
— A selected name whose prefix denotes a signal, add the longest static prefix of the name to the sensitivity set.
— An indexed name whose prefix denotes a signal, add the longest static prefix of the name to the sensitivity set and apply this rule to all expressions in the indexed name.
— A slice name whose prefix denotes a signal, add the longest static prefix of the name to the sensitivity set and apply this rule to any expressions appearing in the discrete range of the slice name.
...
This rule is also used to construct the sensitivity sets of the wait statements in the equivalent process statements for concurrent procedure call statements (11.4), concurrent assertion statements (11.5), and concurrent signal assignment statements (11.6). Furthermore, this rule is used to construct the sensitivity list of an implicit wait statement in a process statement whose process sensitivity list is the reserved word all (11.3).
What this tells us is that the longest static prefix is sufficient in the sensitivity list.
8.1 paragraph 7:
A static signal name is a static name that denotes a signal. The longest static prefix of a signal name is the name itself, if the name is a static signal name; otherwise, it is the longest prefix of the name that is a static signal name. ...
With a non-null range for inp, using parentheses instead of square brackets your mux_41 could be expressed:
library ieee;
use ieee.std_logic_1164.all;
entity mux_41 is
port (
inp: in std_logic_vector (3 downto 0);
outp: out std_logic;
sel: in std_logic_vector (1 downto 0)
);
end entity mux_41;
architecture behavioral of mux_41 is
begin
process (sel, inp)
begin
if sel = "00" then
outp <= inp(0);
elsif sel = "01" then
outp <= inp(1);
elsif sel = "10" then
outp <= inp(2);
else
outp <= inp(3);
end if;
end process;
end architecture behavioral;
Here with the process sensitivity list supplied with the longest static prefix.
This code will analyze (compile) successfully.

VHDL initialize signal with maximum value of type

I have this definition:
subtype sample_t is signed(SAMPLE_WIDTH-1 downto 0);
Now in the code I want to set a signal to the maximum value of sample_t divided by 2:
signal max_sample : sample_t;
max_sample <= to_signed(max_sample'<some attribute>/2,max_sample'LENGTH);
I've looked at attributes sample_t'HIGH and sample_t'RIGHT but these seem to return the highest subscript of the array.
From this list: https://www.csee.umbc.edu/portal/help/VHDL/attribute.html
T'HIGH is the highest value of type T.
A'HIGH is the highest subscript of array A or constrained array type.
How to use the first definition on sample_t?
One of the commenters suggestions:
max_sample <= (max_sample'LEFT => '0', others => '1');
works. But this:
max_sample <= (max_sample'LEFT => '0', others => '1')/2;
fails with "OTHERS is illegal aggregate choice for unconstrained target".
why this error?
In VHDL, signed is an array type, not an integer. The core language only knows that it is a collection of std_logic objects. It's interpretation as a number is only by convention introduced by the numeric_std library functions defining the operators and type casts for signed. As such, integer specific attributes aren't going to work on an array type.
The reason why your last attempt at division fails is that the aggregate expression forming the dividend doesn't yet have a fully determined range due to the others. The aggregate is still just an intermediate temporary and can't pick up its length from max_sample. That prevents the division operator from compiling.
It will compile if you use a fully constrained range:
max_sample <= (max_sample'high => '0', max_sample'high-1 downto max_sample'low => '1') / 2;
Or if you use a qualified expression:
max_sample <= sample_t'(max_sample'high => '0', others => '1') / 2;
An alternate solution is just to subtype integer instead of using signed. Then you can use it in a more integery way:
constant SAMPLE_T_MAX : integer := 2**(SAMPLE_WIDTH-1)-1;
subtype sample_t is integer range -SAMPLE_T_MAX-1 to SAMPLE_T_MAX;
max_sample <= sample_t'high; -- Assuming that the "divide by 2" was just to avoid the sign bit
This will not work if sample_t_max exceeds the largest integer size your tooling supports which is still typically 32-bits including the sign. Also, for practical reasons, it is best not to use integer types for signals that will be exposed on the top level port after synthesis.
Otherwise you have to work within the limitations of using array types for numerics. Your options are to resort to bit twiddling as above or just directly compute the max value similarly to the integer subtype:
constant SAMPLE_T_MAX : integer := 2**(sample_t'length-1)-1;
max_sample <= to_signed(SAMPLE_T_MAX, max_sample'length);
The accepted answer from Kevin Thibedeau could not be readily reconciled with the actual question (why this error?). The answer can be explained in terms of the VHDL standard.
First a Minimum, Complete, and Verifiable example can be constructed for the question:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity foo is
end entity;
architecture fum of foo is
constant SAMPLE_WIDTH: natural := 42; -- pick a number
subtype sample_t is signed(SAMPLE_WIDTH-1 downto 0);
signal max_sample : sample_t;
begin
-- max_sample <= (max_sample'LEFT => '0', others => '1'); -- analyzes
max_sample <= (max_sample'LEFT => '0', others => '1')/2; -- Doesn't analyze
end architecture;
Because we're dealing with semantic rules during analysis (compile) the example doesn't have to do anything besides replicate the error. Error messages aren't standardized in VHDL and will vary by implementation.
The semantics for using an others choice in an aggregate:
IEEE Std 1076-2008 9.3 Aggregates, 9.3.3 Array aggregates, para 7 (in part):
The index range of an array aggregate that has an others choice shall be determinable from the context. That is, an array aggregate with an others choice shall appear only in one of the following contexts:
...
e) As a value expression in an assignment statement, where the target is a declared object (or member thereof), and either the subtype of the target is a fully constrained array subtype or the target is a slice name
This is why the first example (commented out) analyzes. sample_t is a fully constrained subtype, see 5. Types, 5.1 General the definition of fully constrained, para 6 (in part):
A composite subtype is said to be fully constrained if:
-- It is an array subtype with an index constraint and the element subtype either is not a composite subtype or is a fully constrained composite type, or
...
Where the sample_t element base type is std_ulogic and the subtype has an index constraint.
Back to the second example.
We don't satisfy rule e), the aggregate isn't a value expression for the assignment statement, it's an operand for the division operator defined by the "/" function for overload in IEEE package numeric_std:
-- Id: A.25
function "/" (L : UNRESOLVED_SIGNED; R : INTEGER) return UNRESOLVED_SIGNED;
-- Result subtype: UNRESOLVED_SIGNED(L'LENGTH-1 downto 0)
-- Result: Divides an UNRESOLVED_SIGNED vector, L, by an INTEGER, R.
-- If NO_OF_BITS(R) > L'LENGTH, result is truncated to L'LENGTH.
(For earlier revisions of numeric_std the parameter and result types would be SIGNED instead of UNRESOLVED_SIGNED, -2008 formally defines how resolution functions of composite types drivers are defined.)
Because the rules of 9.3.3 paragraph 7 are inclusive instead of exclusive we
need to find a rule that allows us to use the aggregate, and one is readily identified:
i) As the operand of a qualified expression whose type mark denotes a fully constrained array subtype
We can use a qualified expression:
max_sample <= sample_t'(max_sample'LEFT => '0', others => '1')/2;
And this analyzes (compiles) successfully.
To clarify the value expression being assigned to max_sample is the result of division by 2 which uses an operator overload and who's function does not return a constrained subtype. (UNRESOLVED_SIGNED or SIGNED is not constrained).
The reason why there are inclusive rules is to allow the code for associating values with elements of an aggregate to be determined at analysis time (compile time). You'd find by examining previous revisions of the VHDL standard that the list of inclusive rules has been expanded.
The answer to your question with respect to the second example:
max_sample <= (max_sample'LEFT => '0', others => '1')/2;
is that the elements of the aggregate can't be identified without knowing the subtype from context.
If you were to look at the output of the parser producing an abstract syntax tree used for semantic analysis the right hand side expression in the assignment statement can be expressed hierarchically. (11.6 Concurrent signal assignment statements, the waveform is comprised of one or more waveform elements, see 10.5.2 Simple signal assignments, 10.5.2.1 General, a waveform element can contain a value expression, see 10.5.2.2 Executing a simple assignment statement, 9. Expressions, 9.1 General the BNF.)
Using inclusive rules alleviates the need of traversing the expression hierarchy and calculating the subtype of the aggregate (an operand) during analysis and would require semantic characterization of the "/" function not required by the standard. You can also construct cases where the subtype can't be readily ascertained by characterization at analysis time. The result is the rules found in 9.3.3 Array aggregates paragraph 7.
The qualified expression provides the subtype directly.

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.

Can a constant expression ever be valid in a VHDL case statement?

I recall that in Verilog it can be valid to use a constant in the expression of a case statement, but is it also valid in VHDL?
// Verilog example
case(1'b1)
A[2]: Y<=2'b11;
A[1]: Y<=2'b10;
A[0]: Y<=2'b01;
default:Y<=2'b00;
endcase
The following code is not exactly parallel to your example, but it may be useful:
This compiles:
process
constant S : std_logic := '1';
begin
case S is
when '0' =>
when '1' =>
when others =>
end case;
end process;
So does this:
process
begin
case std_logic'('1') is
when '0' =>
when '1' =>
when others =>
end case;
end process;
This does not:
process
begin
case '1' is
when '0' =>
when '1' =>
when others =>
end case;
end process;
'1' is ambiguous (std_logic / character).
Now, what you're actually doing is a bit different, and I'm not sure what your intent is. You seem to want to determine the highest bit that is set. In VHDL, your construct would only be valid syntactically if A is also constant (case choices need to be locally static), in which case there are much easier ways to accomplish this (e.g. a for loop). There's the problem of mutual exclusion, though - in general, case choices are bound to overlap, and I would think that should be illegal (though ModelSim didn't complain when I tried it - synthesis tools may be another story). I would say it's definitely not a great idea either way.
No you can't express the case statement as a VHDL case statement
Unlike Verilog, IEEE Std 1364-2005 9.5 Case statement para 4:
The case expression and the case item expression can be computed at
run time; neither expression is required to be a constant expression.
In IEEE Std 1076-1993 8.8 Case statement, para 8 (also representative of -2008):
The simple expression and discrete ranges given as choices in a case
statement must be locally static. A choice defined by a discrete range
stands for all values in the corresponding range. The choice others is
only allowed for the last alternative and as its only choice; it
stands for all values(possibly none) not given in the choices of
previous alternatives. An element simple name (see 7.3.2 ) is not
allowed as a choice of a case statement alternative.
From the glossary:
locally static expression: An expression that can be evaluated during the analysis of the design unit in which it appears.
analysis: The syntactic and semantic analysis of source code in a VHDL design file and the insertion of intermediate form representations of design units into a design library.
(A fancy way of telling us compile time as opposed to elaboration (linking and loading). VHDL's definitions also embrace interpretive implementation.)
The definition of a locally static expression requires A to be declared as a constant and in the VHDL LRM -1993 version a slice name or index name isn't locally static (See IEEE Std 1076-1993, 7.4.1 Locally static primaries para 1, 7.4.2 Globally static primaries, r. - globally static means at elaboration time). This is changed in the IEEE Std 1076-2008 for static index expressions or static index ranges (See IEEE Std 1076-2008, 9.4.2, n. and o.). It's likely synthesis vendors tools lag, even if you can simulate it, you likely can't synthesis it today.
There's also the issue of multiple choices with the same value. See IEEE Std 1076-1993 8.8 Case statement, para 3 (also representative of -2008 for this issue):
The expression must be of a discrete type, or of a one-dimensional
array type whose element base type is a character type. This type must
be determinable independently of the context in which the expression
occurs, but using the fact that the expression must be of a discrete
type or a one-dimensional character array type. Each choice in a case
statement alternative must be of the same type as the expression; the
list of choices specifies for which values of the expression the
alternative is chosen.
Where the expressions std_logic'('1') or S have a base type of std_ulogic. And this eliminates choices that are not of the base type of std_logic (std_ulogic - 'U', 'X', '0', '1', 'Z', 'W', 'L', 'H', '-', See IEEE Std 1076-2008 16.7 Standard multivalue logic package and Annex A footnote 15 which provides a link to the source for package std_logic_1164).
Notice fru1tbat showed choices '0', '1' and others, evaluation of the expressions A(2), A(1) or A(0) returns a std_ulogic subtype and would give you multiple choices representing the same value ('1'). See IEEE Std 1076-1993, 8.8 para 7:
For other forms of expression, each value of the (base) type of the
expression must be represented once and only once in the set of
choices, and no other value is allowed.
So the case statement isn't directly expressible in VHDL, we're only interested in cases where A(2), A(1) and A(0) are '1' and only one choice is allowed. Instead we can use a conditional signal assignment statement:
library ieee;
use ieee.std_logic_1164.all;
entity constexp is
end entity;
architecture foo of constexp is
signal A: std_logic_vector (2 downto 0) := "001";
signal Y: std_logic_vector (1 downto 0);
begin
COND: -- Conditional Signal Assignment statement
Y <= "11" when A(2) = '1' else
"10" when A(1) = '1' else
"01" when A(0) = '1' else
"00";
end architecture;
(And this design specification analyzes, elaborates and simulates.)
A conditional signal assignment is a concurrent statement in the 1076-1993 standard (See 9.5.1 Conditional signal assignments) while a conditional signal assignment can be used as a sequential statement as well in the 1076-2008 standard (See 11.6 Concurrent signal assignment statements and 10.5.3 Conditional signal assignment). Today it's likely synthesis vendors would be -1993 compliant.
And by definition any of the constructs here can be devolved into if statements which are sequential statements.

Resources