VHDL: is correct to use don't care?

Viewed 3972

I am learning VHDL and for the university I've to write an encoder with priority with different describing styles like DataFlow, Behavioural, etc... The problem is when I have to describe it using statements like "case..when".

The base solution would be something like this:

architecture Behav_arch of encoderw_priority
begin

    case req is
        when "1000" | "1001" | "1010" | "1011" | "1100" | "1101" | "1110" | "1111" =>
            code <= "11";
        when "0100" | "0101" | "0110" | "0111" | "0100" | "0101" | "0110" | "0111" =>
            code <= "10";
        .....

    end case;           

end Behav_arch;

but I proposed this solution, that I think it's more readable:

entity encoderw_priority is
    port(
        req: in std_logic_vector(3 downto 0);
        code: out std_logic_vector(1 downto 0);
        active: out std_logic
    );
end encoderw_priority;

architecture Behav_Case of encoderw_priority is
begin

    process(req)
    begin

        case req is
            when "1---" =>
                code <= "11";
            when "01--" =>
                code <= "10";
            when "001-" =>
                code <= "01";
            when "0001" | "0000" =>
                code <= "00";
            when others =>
                code <= "XX";
         end case;
    
         case req is
            when "0000" =>
                active <= '0';
            when others =>
                active <= '1';
         end case;

    end process;

end Behav_Case;

Is this a correct solution? Simulating this, it works correctly. But I don't know if during synth or implementation it can bring some problems.

Thank you

2 Answers

The classic and easiest solution to this is an "if" statement. I looked at the code referenced by @user1155120 and found it a little more complex than required.

    if req(3) = '1' then 
            code <= "11";
    elsif req(2) = '1' then 
            code <= "10";
    elsif req(1) = '1' then 
            code <= "01";
    else 
            code <= "00";
    end if; 

Certainly case? will also work, but it is VHDL-2008 and you are at the whims of your synthesis vendor for support:

    case? req is
        when "1---" =>
            code <= "11";
        when "01--" =>
            code <= "10";
        when "001-" =>
            code <= "01";
        when "0001" | "0000" =>
            code <= "00";
        when others =>
            code <= "XX";
     end case?;

@EML injected some FUD about driving assigning an 'X' in an assignment in general and in particular here, the others branch of a case statement. @EML was expressing concern about VHDL based on Verilog - which are significantly different in how they handle 'X'.

First conclusions: Assigning 'X' is the correct thing to do. However, when assigning '-' then some of @EML's concerns apply.

General notes: In VHDL, there are three senses of X: 'U', 'X', and '-'.

'U' is intended to be uninitialized. For it to have this meaning, only the simulator is to assign this value (initial value during elaboration). So do not use 'U' in an assignment.

'X' is intended to be an invalid value that results from multiple opposing drivers, however, due to historical reasons, for synthesis 'X', also means don't care when it is assigned to an object (signal, variable, or constant).

'-' is intended to mean don't care. However, with ordinary relationals (=, /=) and regular case statements, it did not have this meaning. VHDL-2008 added matching relationals (?= and ?/=) as well as case? and these understand understand '-' as don't care.

It should be noted that neither the matching relationals nor the case? treat 'X' as don't care - Only synthesis tools only treat 'X' as don't care when it is assigned to an object.

So why should I use 'X' and not '-' for conveying don't care in an assignment? It all comes down to what happens next to that object (signal, ...). For an 'X', if the object is used in the select expression of case? or in a matching relational, the 'X' will never be misunderstood as don't care and will only match when compared with a '-' (in the target of the case? or other argument of a matching relational). Hence, correct behavior is always achieved.

On the other hand, if a '-' is assigned to an object, the analysis gets more interesting. For matching relationals, in simulation, the '-' will incorrectly be understood as a don't care and may result in a mismatch between simulation and synthesis results - this is bad. For case?, a '-' in the expression that follows the case? results in a run time error during simulation. For ordinary relationals (= and /=) and case (without the ?), '-' only matches '-', so there are no hazards using when assigning a '-' to an object.

It is good to see that VHDL has continued in its tradition of robust language design. Yea for VHDL. I am sure glad I don't use Verilog. While VHDL presents a challenge with respect to strong typing, strong typing protects us from doing bad things. Verilog OTOH has many lessons to learn that are subtle and easy to accidentally do. Thanks for the lively discussion @EML.

1 - a 'better' solution might be to simply do a K-map reduction on the required values for req. Your choices seem to be closely related for each branch, so the logic is likely to be minimal. The synthesiser will produce exactly the same result, but you might consider an if-else with simple logic to be 'better' in some way

2 - your case doesn't work; it matches against the std_logic - enumeration, and not against a 'real' don't-care. Of course, you won't have any of these in synthesis, and you're unlikely to have them in simulation, so the conditions will never match

3 - your case also assigns X to code. You mustn't ever do anything like this in synthesis. If the synthesiser treats this as a don't care and chooses some value to assign to code then you'll get synthesis and simulation mismatches. This is a difficult area - see Mike Turpin's paper, for example, or various SNUG Verilog papers

4 - your case should be a case?, as pointed out by user1155120. case? is designed for this sort of thing, and is an improved version of Verilog's casex/z, but I would think twice before using it. The comparison operation is complex (see the ?= table on p122 of the LRM), and unless you're careful, you could get simulation and synthesis mismatches. I would be inclined to stick with the original solution (which is more readable anyway, and easier to change).

Related