vhdlghdl

How to set branch in case statement from a constant? ERROR: choice must be locally static expression


Verilog allows the branches of a case statement to be defined as a constant in a different file. Example:

`define COND1 3'b001
`define COND2 3'b010
`define COND3 3'b100

module xyz(input wire [2:0] select, output reg value);
    always @* 
    case(select)
    `COND1: value = 1'b0;
    `COND2: value = 1'b1;
    `COND3: value = 1'b0;
    default: value = 1'b0;
endmodule

How can I do the same in VHDL? I want to have my constants for the case defined in a package and to pull these constants into the current architecture and use the constants to define the branches for the case statement. Working example:

library ieee;
use ieee.std_logic_1164.all;

entity stuff is
   port(
       sel1: in std_logic_vector(2 downto 0);
       val1:  out std_logic
   );
end entity;

architecture rtl of stuff is
    constant COND1 : std_logic_vector(2 downto 0) := "001";
    constant COND2 : std_logic_vector(2 downto 0) := "010";
    constant COND3 : std_logic_vector(2 downto 0) := "100";
begin

    process(sel1) 
    begin 
        case sel1 is
        when COND1 => val1 <= '0';
        when COND2 => val1 <= '1';
        when COND3 => val1 <= '0';
        when others => val1 <= '0';
        end case;
    end process;

end architecture;

Which works ok...

However, when I try it in my VHDL code I get a strange error:

..\simtools\ghdl\bin\ghdl.exe -a stuff2.vhdl
stuff2.vhdl:40:18: choice must be locally static expression   
stuff2.vhdl:41:18: choice must be locally static expression
stuff2.vhdl:42:18: choice must be locally static expression

Here's the code that gives this error:

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity stuff is

    generic(
        CW : integer := 3
    );
    port(    
        sel1 : in    std_logic_vector(CW-1 downto 0);
        val1 : out   std_logic
    );

end entity;

architecture rtl of stuff is

    function n(n_value:integer; n_width:integer) 
        return std_logic_vector is
    begin
        return std_logic_vector(to_unsigned(n_value, n_width));
    end function;

    constant CMD1 : std_logic_vector(2 downto 0) := n(0, 3);
    constant CMD2 : std_logic_vector(2 downto 0) := n(1, 3);
    constant CMD3 : std_logic_vector(2 downto 0) := n(2, 3);
    constant CMD4 : std_logic_vector(2 downto 0) := n(3, 3);
    constant CMD5 : std_logic_vector(2 downto 0) := n(4, 3);

    signal sel2 : std_logic_vector(2 downto 0);

begin

    sel2 <= sel1(2 downto 0);

    process(sel2)
    begin
        case sel2 is
            when CMD1   => val1 <= '0';     
            when CMD2   => val1 <= '1';     
            when CMD3   => val1 <= '0';     
            when others => val1 <= '0';     
        end case;
    end process;

end architecture;

Solution

  • Kevin Kruse's answer depends on -2008:

    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:

    ...
    e) A function call whose function name denotes an implicitly defined operation or an operation defined in one of the packages STD_LOGIC_1164, NUMERIC_BIT, NUMERIC_STD, NUMERIC_BIT_UNSIGNED, or NUMERIC_STD_UNSIGNED in library IEEE and whose actual parameters are each locally static expressions

    which is not yet implemented as of ghdl-0.36. Otherwise Kevin's answer appears valid for fully -2008 compliant locally static primaries.

    In earlier revisions the constants value expressions are not locally static due to the return value of functions n or to_unsigned.

    See -2002 or earlier 7.4.2 Globally static primaries (9.4.3 - 2008) "i) A function call whose function name denotes a pure function and whose actual parameters are each globally static expressions" where every locally static expression is also globally static.

    The -2008 change adding called functions in IEEE packages that are not allowed to have their function declarations nor functionality changed, allows them to be treated as locally static and is controlled by the copyright licensing terms for the package sources.

    For non-compliant implementations of -2008 or earlier revisions of the standard it's possible to define the numeric value of CMD1 - 4 and convert sel(2 downto 0) to a locally static integer subtype value:

    architecture rtl of stuff is
        constant CMD1:  natural range 0 to 7 := 0;  -- "000"
        constant CMD2:  natural range 0 to 7 := 1;  -- "001"
        constant CMD3:  natural range 0 to 7 := 2;  -- "010"
        constant CMD4:  natural range 0 to 7 := 3;  -- "011"
        constant CMD5:  natural range 0 to 7 := 4;  -- "100"
        signal sel2:    natural range 0 to 7;  -- locally static subtype
    begin
        sel2 <= to_integer(unsigned(sel1(2 downto 0)));
    
        process (sel2)
        begin
            case sel2 is
                when CMD1   => val1 <= '0';
                when CMD2   => val1 <= '1';
                when CMD3   => val1 <= '0';
                when others => val1 <= '0';
            end case;
        end process;
    end architecture;
    

    But the question's first VHDL example most closely implements the Verilog snippet.

    To enable the use of a globally static range of sel1 using a fixed slice for decoding requires the declaration for sel2 to provide a locally static subtype for the case expression:

    architecture equiv_w_generic_sel1 of stuff is
        constant COND1:  std_logic_vector (2 downto 0) := "000";
        constant COND2:  std_logic_vector (2 downto 0) := "001";
        constant COND3:  std_logic_vector (2 downto 0) := "010";
        signal sel2:     std_logic_vector (2 downto 0);
    begin
    
        sel2 <= sel1(sel2'range);  -- locally static subtype
    
        process (sel2)
        begin
            case sel2 is
                when COND1  => val1 <= '0';
                when COND2  => val1 <= '1';
                when COND3  => val1 <= '0';
                when others => val1 <= '0';
            end case;
        end process;
    end architecture;
    

    where you're not redefining the problem to be harder by using non-locally static function calls and also don't require a use clause to provide visibility for package numeric_std declarations. Note the COND1, COND2 and COND3 constants have locally static value expressions as does the Verilog snippet.

    Both of the above architectures analyze with or without ghdl's --std=08 being specified.


    Note the command line shown for ghdl in the question does not specify the VHDL revision and ghdl defaults to the equivalent of --std=93c which provides relaxed compliance matching Modelsim's implementation of the -1993 revision of the standard.