IEEE 200X Fast Track Change Proposal ID: FT-18 Owner: Steve Bailey / Jim Lewis email: Status: Proposed Proposed: 12/03 Analyzed: Date Resolved: Date Related issues: FT-03 (Array/Scalar logic operations) Relevant LRM sections: 7.2 Operators (?? is defined as a unary operator) 7.2.1 Update section to allow boolean/bit binary logic operations 7.2.8 new section to describe semantics of ?? Enhancement/Requirements Summary: -------------------------------------------------------- Simplify the expression of a VHDL condition so that bit types such as bit and std_ulogic (and hence std_logic) can be used in them. Enhancement/Requirements Detail: -------------------------------------------------------- Simplify a VHDL condition and gain consistency between a VHDL conditional and PSL conditional expressions (which also makes PSL conditional expressions in VHDL similar to PSL conditional expressions in Verilog). A VHDL condition is part of the following statements: - assert *condition* ... - if *condition* (if statement and if-generate) - elsif *condition* - wait [...] until *condition* [...] - when *condition* ... (conditional signal assignment, exit, next) - while *condition* - to be defined PSL expressions Required: Support all of the following: Enable future coding styles: if (Cs1 and not nCs2 and Cs3 and Addr=X"A5") then if (Cs1 and nCs2='0' and Cs3 and Addr=X"A5") then if (not nWe) then Keep consistent with current definition of std_ulogic. So active low signal are represented with a not. Sel <= Cs1 and not nCs2 and Cs3 ; Backward compatibility, a condition in an if statement: if (Cs1='1' and nCs2='0' and Cs3='0' and Addr=X"A5") then If ((Cs1 and not nCs2 and Cs3)='1' and Addr=X"A5") then if nWe = '0' then Backward compatibility, boolean signal assignment: signal Y : boolean ; signal nCS : bit ; . . . Y <= nCs = '0' ; -- required boolean = boolean -- Y <= not nCs ; -- not required PSL Understanding of Std_Logic: "In the VHDL flavor, the values STD.Standard.Boolean'(True), STD.Standard.Bit'('1'), IEEE.std_logic_1164.std_logic'('1'), and IEEE.std_logic_1164.std_logic'('H') are all interpreted as the logical value True. Maintain Accuracy of std_logic 'X' handling: S1 = 0 and S2 = X not (S1='1' and S2='1') True (S1 and S2) = '0' False (Most Correct) to_boolean(not(S1 and S2)) False -- note accuracy increases if boolean conversion is -- done as late as possible Maintain Accuracy of expressions of mixed std_logic and boolean: Example Equation: Y <= (not (b and c)) B is type boolean and is TRUE, C = 'X' Y = False (if overloaded logic operators return "bit/sul") Y = True (if overloaded logic operators return boolean) -- inaccurate -- Conclusion, to maintain accuracy, overloading of mixed binary logic -- operators should return bit/sul (or promote boolean to bit/sul as -- appropriate) rather return boolean (or demote bit/sul to boolean). Implementation Summary: ---------------------------- The proposed enhancement is implemented in two parts. Part 1: Create an overloaded unary operator that returns the type boolean that is called implicitly at the outermost level of a condition. If an overloading for the unary operator is visible, the condition can accept the types it supports (and be enhanced beyond currently only supporting boolean). Part 2: Create overloading for binary logic operators between bit types bit/std_ulogic and boolean that return the type bit/std_ulogic. This allows mixed expressions of boolean and a particular type (such as bit or std_ulogic). Implementation Details: ---------------------------- Part 1: Create an overloaded unary operator named "??" that converts from any type to boolean. This function will be called implicitly for any condition that is not already type boolean. Provide a package named boolean_equivalence that will provide overloading for the types bit and std_ulogic. The implicit call overloads a condition so that it will accept types other than boolean. Since the conversion only operates on the top level of an expression, the entire expression must evaluate to a single type. The production for this is: *condition* ::= *boolean*_expression | ??( expression ) Where ?? is called by the compiler when the expression does not resolve to boolean. Further requirements are that there must be a ??" operator visible at the point of the *condition*. library ieee ; use ieee.std_logic.all ; package boolean_equivalence is -- NOTES: -- 1. The package name can be changed. -- function "??"( B : bit ) return boolean; function "??"( S : std_ulogic ) return boolean; end package; package body boolean_equivalence is function "??"( B : Bit ) return BOOLEAN is begin return B = '1'; end function "??"; function "??"( S : std_ulogic ) return BOOLEAN is begin return S = '1' or S = 'H' ; end function "??"; end package; Example: library ieee ; use ieee.boolean_equivalence.all ; . . . if Cs1 and not nCs2 and Cs3 then . . . Part 2: Overloading of binary logic operators: Overload binary logic operators (and, or, nand, nor, xor, xnor) to support the following overloading: -- in std.standard: function "and" (L: bit; R: boolean) return bit; function "and" (L: boolean; R: bit) return bit; function "or" (L: bit; R: boolean) return bit; function "or" (L: boolean; R: bit) return bit; function "xor" (L: bit; R: boolean) return bit; function "xor" (L: boolean; R: bit) return bit; function "nand" (L: bit; R: boolean) return bit; function "nand" (L: boolean; R: bit) return bit; function "nor" (L: bit; R: boolean) return bit; function "nor" (L: boolean; R: bit) return bit; function "xnor" (L: bit; R: boolean) return bit; function "xnor" (L: boolean; R: bit) return bit; -- in ieee.std_logic_1164: function "and" (L: std_ulogic; R: boolean) return std_ulogic; function "and" (L: boolean; R: std_ulogic) return std_ulogic; function "or" (L: std_ulogic; R: boolean) return std_ulogic; function "or" (L: boolean; R: std_ulogic) return std_ulogic; function "xor" (L: std_ulogic; R: boolean) return std_ulogic; function "xor" (L: boolean; R: std_ulogic) return std_ulogic; function "nand" (L: std_ulogic; R: boolean) return std_ulogic; function "nand" (L: boolean; R: std_ulogic) return std_ulogic; function "nor" (L: std_ulogic; R: boolean) return std_ulogic; function "nor" (L: boolean; R: std_ulogic) return std_ulogic; function "xnor" (L: std_ulogic; R: boolean) return std_ulogic; function "xnor" (L: boolean; R: std_ulogic) return std_ulogic; Note, the following overloading is not being created as it will be ambiguous in some situations: -- function "and" (L: boolean; R: boolean) return bit; -- bad signal a, b, c, d : boolean ; . . . -- if (a and b) and (c and d) then ^^^-- should this be a boolean or a bit and or mixture? Example: In a std_ulogic/std_logic context this will accomplish the following (note the last term is boolean): Y <= Cs1 and not nCs2 and Cs3 and Addr = X"A5" ; -- note that if Cs1 = X, nCs2 = 0, Cs3 = 0, and Addr = A5, -- then Y = X Note currently an expression of this form must be coded as: Y <= '1' when Cs1='1' and nCs2='0' and Cs3='1' and Addr = X"A5" else '0' ; -- note that if Cs1 = X, nCs2 = 0, Cs3 = 0, and Addr = A5, -- then Y = 0 Example: Combined with unary operator ??, this allows the following: if Cs1 and not nCs2 and Cs3 and (Addr = X"A5") then -- sul sul sul boolean <<<---- Types The expression results in sul due to binary logic operator overloading, which then gets converted to a boolean using the ?? operator. Discussion: ---------------------------- There has been much discussion on the reflector regarding boolean equivalence. The following summarizes some of the concerns: What about type saftey? Doesn't this weaken strong typing? Currently the following is a syntax error, but with the proposed changes it will be legal: if we_n then Either way, this is a bug. If you write: if we_n then instead of: if not we_n then then you are just as likely to write: if we_n = '1' then instead of: if we_n = '0' then Either way, you must verify your design. It takes education to get hardware engineers to understand: if we_n = '0' then Hardware engineers intuitively understand: if not we_n then Conclusion: Strong typing is not broken. Due to the TRUE and FALSE sense of boolean and '1' and '0' sense of bit, it is very natural that both are allowed to convert or be equivalent under certain situations. It leads to a more natural understanding of boolean expressions. What about negative logic? In negative logic '0' is true and '1' is false. ?? interprets '1' as true and '0' as false. The operation of ?? is consistent with boolean, bit, and std_ulogic. None of these currently support negative logic. Currently if all signals are low true and the output is low true one could write: nSel1 <= not (not nCs1 and not nCs2) ; nSel2 <= nCs1 or nCs2 ; -- Demorgan's equivalent For a conditional, this would be written in a similar way, with the exception that the output of the conditional would need to be understood to be active high: if not nCs1 and not nCs2 then Sel <= '0' ; else Sel <= '1' ; end if ; The call is implicit, part 1. As an alternative, a non-implicit way of implementing the same features as contition? is the following production: condition ::= boolean_expression | boolean_bit_expression | boolean_std_ulogic_expression Then in the LRM define what true means with respect to a boolean_bit_expression and boolean_std_ulogic_expression. This has two disadvantages to the proposed approach. First, this solution cannot be extended without adding another production to the LRM. Second, by not making a package visible that supports the ?? operator, then the above does not apply. The call is implicit part2: There is not obvious trigger in the code. Agreed. User write nothing to cause the call to occur. This makes it a language fact that one needs to know. This is not much different than needing to know the details of any other part of the languge. Note, the following wait statement is often mistaken for a level sensitive check rather than an edge sensitive check: wait until Clk = '1' ; Going further, with strong typing there are other language facts one must acquire to be successful. For example for addition with type unsigned, the size of the result is the same as the size of the largest array input. The call is implicit, part 3: There is some similarity between ?? and overloading. Keep in mind the following is an analogy and that it is not claiming that they are exactly identical. A big issue of this seems to be the implicit nature of the call. However, with overloading, we already do some implicit type things in the language. For example: signal A, Y : unsigned (8 downto 0) ; Y <= A + "000001111" ; vs. Y <= A + 15 ; This is overloading. The compiler picks the right "+" operator to call based on the equation and we are happy. It only knows which one to call based on type of the operands. Likewise: if Cs = '1' and Cs2 = '1' then -- if1 vs. if Cs1 and Cs2 then -- if2 Here the compiler also picks the right "COND" operator for the expression. For the first one (if1), it sees the top expression is boolean so it realizes it does not need to call "COND". For the second one (if2), it sees the top expression results in std_ulogic, so it applies the "COND" operator to convert from std_ulogic to boolean. Again, this is all based on the value of the expression that the top level *conditional* evaluates to. The big difference between the "implicit" call and the handling of overloading is that with overloading, something is always called. Here when the *conditional* evaluates to boolean, nothing is called; when the *conditional* evaluates to bit, the "COND" for bit is called; when the *conditional* evaluates to std_ulogic, the "COND for std_ulogic is called. It seems inconsistent because boolean assignments do not work the same way: signal a, b : bit ; signal y : boolean ; -- Y <= A and B ; -- Illegal. "COND" only applied to *condition* Not that much of an unusual thing to run into in VHDL: signal c, d : unsigned (7 downto 0) ; C <= D + 1 ; -- legal due to overloading "+"(L:unsigned; R:integer) return unsigned ; -- C <= 1 ; -- illegal, integer literal cannot be assigned to unsigned Design entry does not take any significant time, so why make changes? Agreed, but this is not a reason not to make improvements. This argument could be used to say nothing needs to be done to either VHDL with respect to RTL design. This does not fit within my methodology: It is ok not to use this feature. In fact, we are considering putting all of the overloading in a separate package so you can't accidentally do it. However, that does lead to a profusion of packages. This proposal does represent a sound methodology and "does not fit in my methodology" should not prevent it from being part of the language. If you think you have found a testcase that breaks something, and is not addressed here, please make sure to post it to the reflector. Analysis/LRM Changes ---------------------------- Analysis revised on 30 November 2004 by Peter Ashenden: - Changed name of operator from ?? to ??, - Changed declarations of ?? for BIT to be in STANDARD and STD_ULOGIC to be in STD_LOGIC_1164 instead of both in IEEE.BOOLEAN_EQUIVALENCE. - Changed nomenclature: condition operator instead of equivalence operator. - Added specification of where operator fits into expression grammar. Part1: Addition of conditional operator 7.1 Expressions expression ::= [ condition_operator ] logical_expression logical_expression ::= relation { and relation } | ... 7.2 Operators [1st paragraph is unchanged.] condition_operator ::= ?? logical_operator ::= and | or | nand | nor | xor | xnor ... [NOTE: only change is new class of operator (condition operator) and it has the lowest precedence to ensure it is evaluated last] Note 1: The predefined operators for the standard types are declared in package STANDARD as shown in 14.2. The condition operator is not predefined. 7.2.1 Logical operators [binary logical operators are no longer the lowest precedence] NOTE--All of the binary logical operators have the same operator precedence. The unary logical operator *not* belongs to the class of operators with the highest precedence. [NEW] 7.2.8 Condition operator The condition operator is used to define conversion of a type to the type BOOLEAN in package STANDARD. The condition operator must be explicitly declared. The declaration of the condition operator shall have a single input parameter. The base type of the input parameter is the type for which conversion is defined. The return type of the condition operator shall be the type BOOLEAN declared in package STANDARD. The name of the condition operator is "??". The condition operator is explicitly declared for type BIT in the package STANDARD and for the type STD_ULOGIC in the package STD_LOGIC_1164. It may be overloaded for other types. For BIT, appplication of ?? to '1' yields TRUE and to '0' yields FALSE. For STD_ULOGIC, application of ?? to '1' or 'H' yields TRUE and to other values yields FALSE. The condition operator can be used in any context that a type conversion function may be used. As an operator, explicit calls to ?? can be as a unary operator or as a function and must comply to all normal function call and subprogram overload resolution rules. The condition operator shall be implicitly called provided that the following are true: - The context is an expression within a condition context: - if *condition* then - elsif *condition* then - assert *condition* ... - if *condition* generate - until *condition* - when *condition* - while *condition* loop - There is no interpretation of the expression that would satisfy the requirement that the expression in a condition be a boolean expression - There is one ?? operator visible that meets the requirements for unambiguous operator overload resolution in section 2.3. For purposes of overload resolution, the type of the expression is the type of the actual parameter to the ?? operator. Under the circumstances above, the ?? operator is implicitly called to convert the result of the expression in the condition context to a BOOLEAN value. Example: signal Cs1, nCs2, Cs3 : bit ; . . . if Cs1 and not nCs2 and Cs3 then . . . NOTES 1--As the condition operator must be explicitly declared, no condition operators are predefined within the language. It is also not possible to have an implicit call to ?? within a condition context unless an appropriate ?? operator was made visible either through a declaration of ?? within the declarative region or through a use clause. 2--It is possible that the condition expression is a call to an overloaded function where more than one overloading of the function name matches the type(s) of the input parameter(s). If one of these functions has the return type BOOLEAN, then the requirement that the type of the condition expression be BOOLEAN has been satisfied and no implicit call to ?? is made. That is, the overloaded function returning BOOLEAN is chosen. If none of the overloaded functions returns boolean, then according to clause 2.3, the result type can be used to identify exactly one overloaded function. In this case, the overloading is ambiguous if there are visible ?? operators that match the return type of more than one of the overloaded functions. Otherwise, it is unambiguous and the implicit call to ?? applies to convert the result to boolean. 3--Should more than one ?? operator be visible that match the overload resolution rules, then, as specified in clause 2.3, the overloading is ambiguous. 14.2 Package STANDARD Include the following after declaration of type BIT: function "??"( B : bit ) return boolean; Package std_logic_1164 Include the following in conversion functions section: function "??"( S : std_ulogic ) return boolean; Annex A -------- Modify the syntactic production for expression to: expression ::= [ condition_operator ] logical_expression logical_expression ::= relation { and relation } | ... Part 2: Modifications to binary logic operators Change to 1076-2002 Add the following to clause 7.2.1 first paragraph (which probably needs to be split into multiple paragraphs considering the changes proposed also by FT02 and FT03). In addition the binary logic operators AND, OR, NOR, XOR, and XNOR are defined for the case where one operand is of type BOOLEAN and another scalar type. In this case, the return value is the same as the non-boolean scalar type. In section 14.2 add the following function declarations: -- in std.standard: function "and" (anonymous: bit; anonymous: boolean) return bit; function "and" (anonymous: boolean; anonymous: bit) return bit; function "or" (anonymous: bit; anonymous: boolean) return bit; function "or" (anonymous: boolean; anonymous: bit) return bit; function "xor" (anonymous: bit; anonymous: boolean) return bit; function "xor" (anonymous: boolean; anonymous: bit) return bit; function "nand" (anonymous: bit; anonymous: boolean) return bit; function "nand" (anonymous: boolean; anonymous: bit) return bit; function "nor" (anonymous: bit; anonymous: boolean) return bit; function "nor" (anonymous: boolean; anonymous: bit) return bit; function "xnor" (anonymous: bit; anonymous: boolean) return bit; function "xnor" (anonymous: boolean; anonymous: bit) return bit; -- in ieee.std_logic_1164: function "and" (L: std_ulogic; R: boolean) return std_ulogic; function "and" (L: boolean; R: std_ulogic) return std_ulogic; function "or" (L: std_ulogic; R: boolean) return std_ulogic; function "or" (L: boolean; R: std_ulogic) return std_ulogic; function "xor" (L: std_ulogic; R: boolean) return std_ulogic; function "xor" (L: boolean; R: std_ulogic) return std_ulogic; function "nand" (L: std_ulogic; R: boolean) return std_ulogic; function "nand" (L: boolean; R: std_ulogic) return std_ulogic; function "nor" (L: std_ulogic; R: boolean) return std_ulogic; function "nor" (L: boolean; R: std_ulogic) return std_ulogic; function "xnor" (L: std_ulogic; R: boolean) return std_ulogic; function "xnor" (L: boolean; R: std_ulogic) return std_ulogic; Resolution: ---------------------------- [To be performed by the 200X Fast Track Working Group]