DFiantHDL / DFHDL

DFiant HDL (DFHDL): A Dataflow Hardware Descripition Language
https://dfianthdl.github.io/
GNU Lesser General Public License v3.0
78 stars 8 forks source link

Array type is not generated for vectors (VHDL) #128

Closed MartinC45 closed 5 months ago

MartinC45 commented 5 months ago

Description

The generated code of the entity uses the types t_array_elem0 / t_array_elem1/t_array_elem2, which do not exist in the associated package.

I am also wondering what multidimensional arrays would look like. Either
type \<name> is array (natural range \<>, natural range \<>, ...) of \<element_type> and indexed as e.g. arr(0, 0, ...) or type \<name1> is array (natural range \<>) of \<element_type> type \<name2> is array (natural range \<>) of \<name1>
and indexed as e.g. arr(0)(0)...

Example

//> using scala 3.4.0
//> using dep io.github.dfianthdl::dfhdl::0.4.6
//> using plugin io.github.dfianthdl:::dfhdl-plugin:0.4.6
//> using option -deprecation -language:implicitConversions

import dfhdl.*

class ArrayIssue() extends RTDesign:
    val a = Bit <> IN
    val b = Bit X 6 <> VAR
    val c = Bit X 5 X 4 <> VAR
    val d = Bits(4) X 3 X 2 <> VAR

    b(0) := a
    c(0)(0) := a
    d(0)(0)(0) := a

given options.CompilerOptions.PrintGenFiles = true
given options.CompilerOptions.Backend = backends.vhdl

@main def main = 
   ArrayIssue().compile

Output

If c and d are indexed as in the VHDL code it would require the latter variant of multidimensional arrays above.

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

entity ArrayIssue is
port (
  a : in std_logic
);
end ArrayIssue;

architecture ArrayIssue_arch of ArrayIssue is
  signal b : t_array_elem0(0 to 6 - 1);
  signal c : t_array_elem1(0 to 4 - 1);
  signal d : t_array_elem2(0 to 2 - 1);
begin
  process (all)
  begin
    b(0) <= a;
    c(0)(0) <= a;
    d(0)(0)(0) <= a; 
  end process;
end ArrayIssue_arch;
soronpo commented 5 months ago

The generated code of the entity uses the types t_array_elem0 / t_array_elem1/t_array_elem2, which do not exist in the associated package.

Yeah, it's a known issue, but was held up to find a solution for embedded generics parameters, especially in ports. The main issue arises if we have an embedded generic that sets an array's element length on a port. VHDL has a limitation that does not enable us to compile it as easily as Verilog does:

class Foo(arg: Int <> CONST) extends RTDesign:
  val x = UInt(arg) X 8 <> IN

It's illegal in VHDL to do array(0 to 7) of unsigned(arg-1 downto 0), so not sure what's the best course of action here. I could just inline the parameters for VHDL. It hurts the generality of it, but that's a VHDL limitation 🤷

soronpo commented 5 months ago

I am also wondering what multidimensional arrays would look like. Either type is array (natural range <>, natural range <>, ...) of and indexed as e.g. arr(0, 0, ...)

Multi-dimentional arrays in DFHDL would look like val x = UInt(8) X (5,5,5) <> VAR x(0,0,0). The Scala type representation of it actually already exists, but nothing in the frontend API. It's currently on the backburner since usually it's just as fine to write UInt(8) X 5 X 5 X 5.

MartinC45 commented 5 months ago

It's illegal in VHDL to do array(0 to 7) of unsigned(arg-1 downto 0), so not sure what's the best course of action here. I could just inline the parameters for VHDL. It hurts the generality of it, but that's a VHDL limitation 🤷

In VHDL2008, it should be working (1). So the problem is VHDL93 really, where arrays (and everything else) can only be constrained at the outermost level. While it might be possible to have a workaround in some cases, like flattening an array of std_logic_vectors (e.g. std_logic_vector(width * depth - 1 downto 0)), I'd argue it probably is best, to define the generics in the package and constrain things there (2). I think that doing so also aligns with the expectations of someone using VHDL93. The generic constraining the outermost level, e.g. widthArr in (1), could be kept in the entity (3).

What do you think?

(1)

-- package
library ieee;
use ieee.std_logic_1164.all;

package array_port_package is
    type t_array is array(natural range <>) of std_logic_vector;
end package;

-- entity
entity array_port is
    generic (
        widthSLV : integer := 32;
        widthArr : integer := 8
    );
    port (
        arrP : in t_array(0 to widthArr - 1)(widthSLV-1 downto 0);
        out_a : out std_logic
    );
end entity;

(2)

library ieee;
use ieee.std_logic_1164.all;

package array_port_package is
    constant instancename_entityname_genericname : integer := 32;
    type t_array_etc is array (natural range 0 to instancename_entityname_genericname) of std_logic_vector(instancename_entityname_genericname-1 downto 0);
end package;

(3)

-- package
library ieee;
use ieee.std_logic_1164.all;

package array_port_package is
    constant array_port_widthSLV : integer := 32;
    type t_array is array(natural range <>) of std_logic_vector(widthSLV-1 downto 0);
end package;

-- entity
entity array_port is
    generic (
        widthArr : integer := 8
    );
    port (
        arrP : in t_array(0 to widthArr - 1);
        out_a : out std_logic
    );
end entity;