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
Related
I'm watching a beginner tutorial on VHDL. The lecturer says that it's bad practice to not include an else-condition inside a "process" block because it will create a latch and is bad for timing in advance circuits. He said that including an else-condition will create a mux instead and is better to use in most case.
Why is that?
snippet from lecture video
Why is a latch design bad for timing and what makes the mux design better?
The point is to make VHDL code that results in the design you want, and the risk is that you will inadvertently create a latch.
There are several basic constructions in VHDL, that can illustrate this.
Mux by process with if-then-else, can be made with this code:
process (all) is -- Mux
begin
if sel = '0' then
z <= a;
else
z <= b;
end if;
end process;
Mux by process with if-then and default assign, can be made with this derived code:
process (all) is -- Mux
begin
z <= b; -- Default b, thus for sel = '1', since no overwrite
if sel = '0' then
z <= a;
end if;
end process;
However, if you want to make a mux, then a better coding style is by continuous assign with this code:
z <= a when (sel = '0') else b;
Finally, what can lead to a latch with a process, is if the resulting output is not assigned in all branches of the code. That can occur, if the if-then does neither have an else, nor a default assign to the output, like this code:
process (all) is -- Latch
begin
if en = '1' then
q <= d;
end if;
end process;
So a good rule when designing combinatorial logic using a process, is to have an initial line that makes a default assignment to the resulting output, for example just assing undefined 'X'. A latch is thereby avoided, and functional verification should catch the undefined 'X', if this is a bug.
Also, remember to check the syntheis report for warnings about created latches, since that is most likely a bug in the design.
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.
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).
timescale 1ns/10ps
/* resource counter for nor gates
*/
module global_vars;
integer count;
endmodule
module my_nor(y, a, b);
output y;
input a, b;
global_vars gv;
/* at instantiation increment the resources used */
gv =gv +1;
/* add 2ns inherent delay */
nor #2 nor1(y,a,b);
endmodule
When I compile, there is one syntax error at global_vars gv;
I have no idea. Should I initial the gv?
In verilog you can not just do this:
gv = gv +1;
wire types need to use assign:
wire gv;
assign gv = a + b;
reg types can use initial, always #* or always #(posedge clk).
always #* begin
gv = a + b;
end
Your trying to use an instance like a variable, I am not sure what your trying to do with your global_vars, may be make a global variable but creating an instance would make it local not global. Here you would do just as well to make gv an integer rather than an instance.
Note
Wire assignment and always #* are combinatorial, is there is no time delay in the assignment, therefor the value can not be directly referenced to itself. For example
gv = gv + 1;
Is a combinatorial loop, when do you expect the +1 to happen. This is normally solved by making gv a flip-flop and updating its value on a clock edge:
always #(posedge clk) begin
gv <= gv + 1;
end
In this case you still need to set an initial value for gv. for FPGAs this can be done using an initial or an async reset for ASIC.
FPGA using initial:
initial begin
gv = 'b0;
end
always #(posedge clk) begin
gv <= gv + 1;
end
Or for ASIC using reset:
always #(posedge clk or negedge rst_n) begin
if (~rst_n) begin
gv <= 'b0;
end
else begin
gv <= gv + 1;
end
end
It looks like you are trying to embed a reference to a global variable within a synthesizable module so that you can count how many times that module has been instantiated. As #Morgan has already pointed out, you can't reference a module instantiate as a variable. But there is an even more fundamental problem -- you are trying to mix behavioral code (i.e. global variables) into synthesizable code and that just doesn't work.
Creating a synthesized module is really a two step process -- design and then coding. In the design phase, you figure out what kind of logic you are trying to create. You don't need to go down to the gate level, but you should be able to sketch out the function with clouds of combinatorial logic, registers, FIFOs, memories etc. Once you've gained some familiarity with the language, this may strictly be a mental process rather than something committed to paper, but you still need to think about the logic you are creating. Then once you have designed your circuit, you actually go write the code to implement your design.
Global variables do not exist in hardware. You can make a counter that is accessible by multiple modules, but but it would need some sort of arbitration process. But what you're trying to do is create a run-time counter that counts up the number of modules instantiated, for which there is no hardware analog. You could use 'define statements to instantiate a variable number of modules and have a signal from each module which is defined as '1' if the module is instantiated and '0' if it isn't and then count the number of '1's, but that is self defeating since you already had to define a macro telling you the number of modules before you instantiated them! In which case, you might as well just use the macro value directly and dispense with all the extra signals.
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.