nickg / nvc

VHDL compiler and simulator
https://www.nickg.me.uk/nvc/
GNU General Public License v3.0
589 stars 75 forks source link

2008: Cannot lower type kind T_GENERIC #883

Closed bpadalino closed 1 month ago

bpadalino commented 2 months ago

The following code:

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

package generic_complex is generic (
    type T ;

    -- Required for being able to work with the type
    function pack(x : in T) return std_ulogic_vector is <> ;
    function unpack(x : in std_ulogic_vector) return T is <> ;
    function resize(x : in T ; size : in positive) return T is <> ;
    function length(x : in T) return natural is <> ;
    function make(size : positive) return T is <>
) ;

    type complex is record
        re : T ;
        im : T ;
    end record ;

    function length(x : in complex) return natural ;

    function pack(x : in complex) return std_ulogic_vector ;
    function unpack(x : in std_ulogic_vector) return complex ;

    function resize(x : in complex ; size : in positive) return complex ;

    function cmake(size : in positive) return complex ;

end package ;

library ieee;
    use ieee.std_logic_1164.all ;

use work.generic_complex ;

package generic_complex_components is

    component complex_mult is
      generic (
        package complex is new work.generic_complex generic map (<>)
      ) ;
      port (
        clock   :   in  std_ulogic ;
        reset   :   in  std_ulogic ;
        a       :   in  complex.complex ;
        b       :   in  complex.complex ;
        c       :   out complex.complex
      ) ;
    end component ;

end package ;

package body generic_complex is

    function length(x : in complex) return natural is
    begin
        return length(x.re) ;
    end function ;

    function pack(x : in complex) return std_ulogic_vector is
        constant re : std_ulogic_vector := pack(x.re) ;
        constant im : std_ulogic_vector := pack(x.im) ;
    begin
        return im & re ;
    end function ;

    function unpack(x : in std_ulogic_vector) return complex is
        constant PART_SIZE : positive := x'length/2 ;
        subtype RE_RANGE is natural range PART_SIZE-1 downto 0 ;
        subtype IM_RANGE is natural range 2*PART_SIZE-1 downto PART_SIZE ;
        constant rv : complex := (re => unpack(x(RE_RANGE)), im => unpack(x(IM_RANGE))) ;
    begin
        return rv ;
    end function ;

    function resize(x : in complex ; size : in positive) return complex is
        constant rv : complex := (re => resize(x.re, size), im => resize(x.im, size)) ;
    begin
        return rv ;
    end function ;

    function cmake(size : in positive) return complex is
    begin
        return complex'(re => make(size), im => make(size)) ;
    end function ;

end package body ;

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

entity dsp45 is
  port (
    clock   :   in  std_ulogic ;
    reset   :   in  std_ulogic ;
    a       :   in  std_ulogic_vector(17 downto 0) ;
    b       :   in  std_ulogic_vector(26 downto 0) ;
    c       :   out std_ulogic_vector(44 downto 0) ;
    d       :   out std_ulogic_vector(44 downto 0) -- extra port for this one
  ) ;
end entity ;

architecture pipelined of dsp45 is

begin

    process(clock)
    begin
        if(rising_edge(clock)) then
            if( reset = '1' ) then
                c <= (others =>'U') ;
            else
                c <= std_logic_vector(resize(signed(a)*signed(b), c'length)) ;
            end if ;
        end if ;
    end process ;

end architecture ;

architecture combinatorial of dsp45 is

begin

    c <= std_logic_vector(resize(signed(a)*signed(b), c'length)) ;
    d <= std_logic_vector(resize(signed(a)*"-"(signed(b)), c'length)) ;

end architecture ;

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

entity dsp51 is
  port (
    clock   :   in  std_ulogic ;
    reset   :   in  std_ulogic ;
    a       :   in  std_ulogic_vector(23 downto 0) ;
    b       :   in  std_ulogic_vector(26 downto 0) ;
    c       :   out std_ulogic_vector(50 downto 0)
  ) ;
end entity ;

architecture pipelined of dsp51 is

begin

    process(clock)
    begin
        if( rising_edge(clock)) then
            if( reset = '1' ) then
                c <= (others =>'U') ;
            else
                c <= std_logic_vector(resize(signed(a)*signed(b), c'length)) ;
            end if ;
        end if ;
    end process ;

end architecture ;

library ieee ;
    use ieee.std_logic_1164.all ;

entity complex_mult is
  generic (
    package complex is new work.generic_complex generic map (<>)
  ) ;
  port (
        clock   :   in  std_ulogic ;
        reset   :   in  std_ulogic ;

        a       :   in  complex.complex ;
        b       :   in  complex.complex ;
        c       :   out complex.complex
  ) ;
end entity ;

architecture dsp45_pipelined of complex_mult is

    constant DSP45_A_SIZE : positive := 18 ;
    constant DSP45_B_SIZE : positive := 27 ;
    constant DSP45_C_SIZE : positive := 45 ;
    subtype DSP45_C_RANGE is natural range DSP45_C_SIZE-1 downto 0 ;

    signal csig : std_ulogic_vector(DSP45_C_RANGE) ;

begin

    U_dsp : entity work.dsp45(pipelined)
      port map (
        clock   =>  clock,
        reset   =>  reset,

        a       =>  complex.pack(complex.resize(a, DSP45_A_SIZE)),
        b       =>  complex.pack(complex.resize(b, DSP45_B_SIZE)),
        c       =>  csig,
        d       =>  open
      ) ;

    c <= complex.resize(complex.unpack(csig), complex.length(c)) ;

end architecture ;

architecture dsp45_combinatorial of complex_mult is

    constant DSP45_A_SIZE : positive := 18 ;
    constant DSP45_B_SIZE : positive := 27 ;
    constant DSP45_C_SIZE : positive := 45 ;
    subtype DSP45_C_RANGE is natural range DSP45_C_SIZE-1 downto 0 ;

    signal csig : std_ulogic_vector(DSP45_C_RANGE) ;

begin

    U_dsp : entity work.dsp45(combinatorial)
      port map (
        clock => clock,
        reset => reset,

        a => complex.pack(complex.resize(a, DSP45_A_SIZE)),
        b => complex.pack(complex.resize(b, DSP45_B_SIZE)),
        c => csig,
        d => open
      ) ;

    c <= complex.resize(complex.unpack(csig), complex.length(c)) ;

end architecture ;

architecture dsp51_pipelined of complex_mult is

    constant DSP51_A_SIZE : positive := 24 ;
    constant DSP51_B_SIZE : positive := 27 ;
    constant DSP51_C_SIZE : positive := 51 ;
    subtype DSP51_C_RANGE is natural range DSP51_C_SIZE-1 downto 0 ;

    signal csig : std_logic_vector(DSP51_C_RANGE) ;

begin

    U_dsp : entity work.dsp51(pipelined)
      port map (
        clock   =>  clock,
        reset   =>  reset,

        a       => complex.pack(complex.resize(a, DSP51_A_SIZE)),
        b       => complex.pack(complex.resize(b, DSP51_B_SIZE)),
        c       => csig
      ) ;

    c <= complex.resize(complex.unpack(csig), complex.length(c)) ;

end architecture ;

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

    use work.generic_complex_components.all ;

entity test is
end entity ;

architecture arch of test is

    function pack(x : in signed) return std_ulogic_vector is
    begin
        return std_ulogic_vector(x) ;
    end function ;

    function unpack(x : in std_ulogic_vector) return signed is
    begin
        return signed(x) ;
    end function ;

    function length(x : in signed) return natural is
    begin
        return x'length ;
    end function ;

    function make(size : in positive) return signed is
    begin
        return to_signed(0, size) ;
    end function ;

    package complex_signed is new work.generic_complex generic map (t => signed) ;

    signal clock : std_ulogic := '1' ;
    signal reset : std_ulogic := '1' ;

    signal a, b : complex_signed.complex(re(15 downto 0), im(15 downto 0)) ;
    signal c    : complex_signed.complex(re(31 downto 0), im(31 downto 0)) ;

    signal x : complex_signed.complex(re(11 downto 0), im(11 downto 0)) ;
    signal y : complex_signed.complex(re(22 downto 0), im(22 downto 0)) ;
    signal z : complex_signed.complex(re(33 downto 0), im(33 downto 0)) ;

begin

    clock <= not clock after 1 ns ;

    U_mult_ab : component complex_mult
      generic map (
        complex => complex_signed
      ) port map (
        clock   => clock,
        reset   => reset,
        a       => a,
        b       => b,
        c       => c
      ) ;

    U_mult_xy : component complex_mult
      generic map (
        complex => complex_signed
      ) port map (
        clock => clock,
        reset => reset,
        a     => x,
        b     => y,
        c     => z
      ) ;

    tb : process
    begin
        reset <= '0' after 100 ns ;
        wait for 500 ns ;
        std.env.stop ;
    end process ;

end architecture ;

configuration top of test is
    for arch
        for U_mult_ab : complex_mult
            -- possible to remap here
            use entity work.complex_mult(dsp45_combinatorial)
              port map (
                clock => '-',
                reset => '-',
                a     =>  a,
                b     =>  b,
                c     =>  c
              ) ;
        end for ;
        for U_mult_xy : complex_mult
            use entity work.complex_mult(dsp51_pipelined)
              port map (
                clock => clock,
                reset => reset,
                a     => b,
                b     => a,
                c     => c
              ) ;
        end for ;
    end for ;
end configuration ;

... errors with the following:

$ nvc --std=2008 -a crash.vhdl -e test -r
** Fatal: cannot lower type kind T_GENERIC
[0x55a7abe0b41c] ../src/diag.c:1014 diag_femit
[0x55a7abe0b41c] ../src/diag.c:1039 diag_emit
[0x55a7abd49362] ../src/util.c:585 fatal_trace
[0x55a7abdcc579] ../src/lower.c:679 lower_type.lto_priv.0
[0x55a7abdcce67] ../src/lower.c:742 lower_signal_type.lto_priv.0
[0x55a7abdcd01f] ../src/lower.c:734 lower_signal_type.lto_priv.0
[0x55a7abda7e11] ../src/lower.c:11929 lower_instance
[0x55a7abda7e11] ../src/lower.c:1472 elab_lower.lto_priv.0
[0x55a7abdab5a4] ../src/elab.c:1637 elab_component.lto_priv.0
[0x55a7abdb2d4c] ../src/elab.c:1665 elab_stmts.lto_priv.0
[0x55a7abdaa699] ../src/elab.c:1589 elab_architecture.lto_priv.0
[0x55a7abe7ae99] ../src/elab.c:2176 elab_top_level.constprop.0
[0x55a7abdaf521] ../src/elab.c:2238 elab
[0x55a7abd51fda] ../src/nvc.c:464 elaborate
[0x55a7abd4f1df] ../src/nvc.c:1922 process_command
[0x55a7abd4f61c] ../src/nvc.c:1042 init_cmd
[0x55a7abd4f61c] ../src/nvc.c:1936 process_command
[0x55a7abd46df5] ../src/nvc.c:2060 main

Something very well might be wrong with my code. Riviera fails with an error: The type of the actual in the port map does not match the "complex" type of the "~complex~1329" port. Questa fails with a segfault. ghdl fails with an assertion failure, though I am not sure if they really support generic packages fully.

nickg commented 2 months ago

Generic types/packages with components and configurations don't work very well at the moment. Here's another example:

entity sub is
    generic (
        type t;
        function "+" (a, b : t) return t is <> );
    port (
        x, y : in t;
        z : out t );
end entity;

architecture test of sub is
begin
    z <= x + y;
end architecture;

-------------------------------------------------------------------------------

entity top is
end entity;

architecture test of top is
    component sub is
        generic (
            type t;
            function "+" (a, b : t) return t is <> );
        port (
            x : in t;
            z : out t );
    end component;

    signal xr, zr : real;
    signal xi, zi : integer;
begin

    u1: sub generic map ( real ) port map ( xr, zr );

    -- u2: sub generic map ( integer ) port map ( xi, zi );

end architecture;

-------------------------------------------------------------------------------

configuration gentype8 of top is
    for test
        for u1 : sub use entity work.sub
            port map ( x => x, y => x, z => z );
        end for;
    end for;
end configuration;

Questa also crashes on this. GHDL reports an error:

../test/regress/gentype8.vhd:45:29:error: expression associated before its type
            port map ( x => x, y => x, z => z );
                            ^

This seems wrong though, the type should come from the generic map on the instance.

nickg commented 1 month ago

This no longer crashes, but produces this error when run:

** Fatal: 0ms+0: actual length 54 does not match port B length 27
     > /home/nick/nvc/test/regress/issue883.vhd:141
     |
 141 |     b       :   in  std_ulogic_vector(26 downto 0) ;
     |     ^

I haven't checked whether it's actually valid or not. The error comes from the complex.pack(complex.resize(a, DSP51_A_SIZE)) expression in the port map, I'll try to improve the reporting here a bit.

nickg commented 1 month ago

I've improved the error message so it points at the complex.pack call. I think the problem is that function returns a value 2x the width of the port, perhaps it should be using DSP51_A_SIZE/2? If I fix those up it runs now.