0
\$\begingroup\$

I have a vhdl component with a custom type defined for a port. The type is an array of a composite record type. The array size is adjustable via generic and according to the person who designed the component, I can set it anywhere from zero to eight. I would like to set the size to zero to disable it but I'm having trouble getting it to synthesize because I cannot find an acceptable way to assign the input ports.

So how do I assign a zero-length input vector on a component instantiation?

Below are some code snippets of what I have to aid with understanding my question.

Type declarations:

type axi_out_type is record
    aready : std_logic;
end record axi_out_type;

type axi_out_vector is array (natural range <>) of axi_out_type;

type axi_in_type is record
    wid    : std_logic_vector(3 downto 0);
    wdata  : std_logic_vector(1023 downto 0);
    wstrb  : std_logic_vector(127 downto 0);
    wlast  : std_logic;
    wvalid : std_logic;
end record axi_in_type;

type axi_in_vector is array (natural range <>) of axi_in_type;

Component declaration:

component my_component is
generic(
    ...
    NB_AXI_SLV: in integer range 0 to 8 := 1;
    ...
);
port(
    ...
    axi_in_ports: in axi_in_vector(NB_AXI_SLV-1 downto 0);
    axi_out_ports: out axi_out_vector(NB_AXI_SLV-1 downto 0);
    ...
);

signal my_axi_in : axi_in_type;

Component instantiation:

comp: my_component
generic map(
    ...
    NB_AXI_SLV => 0,
    ...
)
port map(
    ...
    axi_in_ports => (others => my_axi_in),  --What do I assign here?!?!
    axi_out_ports => open,
    ...
)

my_axi_in <= (wid=>(others => '0'),wdata=>(others => '0'),
                wstrb=>(others => '0'),wlast=>'0',wvalid=>'0');
\$\endgroup\$
  • \$\begingroup\$ You can use the (others => xyz) syntax to match a null array. xyz can be an aggregate that composes your expected AXI record or as a better solution a constant representing an empty record value. \$\endgroup\$ – Paebbels Nov 6 '17 at 18:39
  • \$\begingroup\$ @Paebbels I tried that but I keep getting 'formal port and actual agree on type but not on size' \$\endgroup\$ – kjgregory Nov 6 '17 at 18:45
  • \$\begingroup\$ What tool and VHDL standard are you using? \$\endgroup\$ – Paebbels Nov 6 '17 at 18:46
  • \$\begingroup\$ Synplify Pro L-2016.03 using VHDL-2008 \$\endgroup\$ – kjgregory Nov 6 '17 at 18:53
  • \$\begingroup\$ IEEE Std 1076-2008, 6.5.6.3 Port clauses, It is an error if a port of mode in is unconnected (see 6.5.6.3) or unassociated (see 6.5.7.3) unless its declaration includes a default expression (see 6.5.2). We don't know the element type of axi_in_vector here. A static expression of (others => element_value) associated with no elements. An aggregate with an others choice may be used (9.3.3.3 Array aggregates) As an actual associated with a ... formal port ..., where ... the formal ... is declared to be of a fully constrained array subtype, which the port declaration does. \$\endgroup\$ – user8352 Nov 6 '17 at 19:59
0
\$\begingroup\$

Constructing a Minimal, Complete and Verifiable example using the snippets provided in the question and Paebbels suggestion of associating a constant with the port to be unconnected analyzes, elaborates and simulates:

library ieee;
use ieee.std_logic_1164.all;

package my_axi_pkg is
    type axi_out_type is record
        aready : std_logic;
    end record axi_out_type;

    type axi_out_vector is array (natural range <>) of axi_out_type;

    type axi_in_type is record
        wid    : std_logic_vector(3 downto 0);
        wdata  : std_logic_vector(1023 downto 0);
        wstrb  : std_logic_vector(127 downto 0);
        wlast  : std_logic;
        wvalid : std_logic;
    end record axi_in_type;

    type axi_in_vector is array (natural range <>) of axi_in_type;
end package;

use work.my_axi_pkg.all;

entity  my_component is
    generic (
        NB_AXI_SLV: in integer range 0 to 8 := 1

    );
    port (

        axi_in_ports: in axi_in_vector(NB_AXI_SLV-1 downto 0);
        axi_out_ports: out axi_out_vector(NB_AXI_SLV-1 downto 0)
    );
end entity my_component;
architecture foo of my_component is
begin
    process
    begin
        report "axi_in_ports'LEFT = " & integer'image(axi_in_ports'LEFT);
        wait;
    end process;
end architecture;

library ieee;
use ieee.std_logic_1164.all;
use work.my_axi_pkg.all;

entity my_component_tb is
end entity;

architecture foo of my_component_tb is

    component my_component is
        generic (
            NB_AXI_SLV: in integer range 0 to 8 := 1

        );
        port (
            axi_in_ports: in axi_in_vector(NB_AXI_SLV-1 downto 0);
            axi_out_ports: out axi_out_vector(NB_AXI_SLV-1 downto 0)
        );
    end component;

    constant NB_AXI_SLV:    natural := 0;  -- ADDED
    constant my_axi_in : axi_in_type := (wid => (others => '0'), 
                                         wdata => (others => '0'),
                                         wstrb => (others => '0'),
                                         wlast =>'0', 
                                         wvalid => '0');

begin
    comp: 
        my_component
            generic map (
                NB_AXI_SLV => NB_AXI_SLV
            )
            port map (
                axi_in_ports => (others => my_axi_in),
                axi_out_ports => open
            );

end architecture;

And that procduces:

my_component.vhdl:39:9:@0ms:(report note): axi_in_ports'LEFT = -1

You could note the value of NB_AXI_SLV is 0.

You could note the constant my_axi_in is an unspecified array type whose element type is axi_in_type and is compatible with axi_in_ports which is type axi_in_vector. The others choice is available for the default value of a signal or the initial value of a variable or constant.

Every element of of axi_in_ports is associated with the default value, possibly (as in this case) none by the others choice.

This malleability is why you want to use an aggregate with an others choice.

How can we have an array bound that doesn't meet the bounds of the index type of axi_in_vector (natural)?

As Paebbels points out there's nothing stopping you from using any integer value here falling within the range INTEGER'LOW to INTEGER'HIGH.

You can't necessarily evaluate an element of such an array definition based on it's bounds.

Duplicating the architecture and adding an identically ranged object of type axi_in_vector and attempting to use it's indexes will fail:

architecture fum of my_component_tb is

    component my_component is
        generic (
            NB_AXI_SLV: in integer range 0 to 8 := 1

        );
        port (
            axi_in_ports: in axi_in_vector(NB_AXI_SLV-1 downto 0);
            axi_out_ports: out axi_out_vector(NB_AXI_SLV-1 downto 0)
        );
    end component;
    constant NB_AXI_SLV:    natural := 0;  -- ADDED

    constant my_axi_in : axi_in_type := (wid => (others => '0'),
                                         wdata => (others => '0'),
                                         wstrb => (others => '0'),
                                         wlast =>'0',
                                         wvalid => '0');

    constant axi_in_vector_local: axi_in_vector(NB_AXI_SLV-1 downto 0) :=
                            (others => my_axi_in);
    signal axi_in_left: axi_in_type;

begin
    comp: 
        my_component
            generic map (
                NB_AXI_SLV => NB_AXI_SLV  -- WAS 0
            )
            port map (
                axi_in_ports => (others => my_axi_in),
                axi_out_ports => open
            );
WHEN_NOT_NULL:
    if NB_AXI_SLV > 0 generate
        axi_in_left <= axi_in_vector_local(axi_in_vector_local'left);
    end generate;

end architecture;

Any place you use a slice or an index with a value out of range will produce a bounds error:

my_component.vhdl:121:64:error: static expression violates bounds

Where the analyzer is complaining about axi_in_vector_local'left and axi_in_vector_local is declared using the local constant NB_AXI_SLV (mapped to the generic in the component instantiation).

It's not a matter of evaluating a value used as an index, rather evaluating an index name (or slice name) using such a value which doesn't meet the index constraint for type axi_in_vector.

So an MCVe can short circuit a large volume of comments easily taken out of context, particularly across edits.

The operative mechanism here is using an aggregate with an others choice where others stands for any remaining elements, possibly none, acceptable as an actual to a formal that's a null array.

There's no universal guarantee that a null array is acceptable for synthesis. A lack of interest by vendors in participating in it's revision allowed IEEE Std 1076.6-2004 (RTL Synthesis) to be withdrawn as outdated.

We see at least one version of Synplify Pro allows the use of null arrays as port types:

Synplify Pro for Microsemi Edition HDL Reference

Chapter 3
VHDL Language Support
Language Constructs
Partially-supported VHDL Language Constructs
Null Ranges

Support for null ranges allows ports with negative ranges to be compiled successfully. During compilation, any port declared with a null range and its related logic are removed by the compiler.

The alternative when a null array is not supported would be to provide a generate statement and two different entity declarations (and their associated architectures), using (in -2008) an if generate statement.

You could also note above that the error occurs when violating the bounds in an index name (or presumably a slice name) even though it's inside a generate statement. Whether code is present in an elaborated design network or not has no bearing on the requirement that when analyzed (compiled) the code is syntactically and semantically valid. The standard gives permission to evaluate locally static expressions during synthesis (and here NB_AXI_SLV is a local constant).

That can be cured by a top level generic (where supported) providing an initial value compatible with expected use, where the generic constant is not evaluated until elaboration. Failing that the only recourse would be a separate architecture.

Moving the constant declaration for NB_AXI_SLV to the my_component_tb entity declaration appearing in a generic clause eliminates the above error, the example analyzes elaborates and simulates:

entity my_component_tb is
    generic ( constant NB_AXI_SLV:    natural := 0);  -- ADDED
end entity;

architecture foo of my_component_tb is

    component my_component is
        generic (
            NB_AXI_SLV: in integer range 0 to 8 := 1

        );
        port (
            axi_in_ports: in axi_in_vector(NB_AXI_SLV-1 downto 0);
            axi_out_ports: out axi_out_vector(NB_AXI_SLV-1 downto 0)
        );
    end component;

    constant NB_AXI_SLV:    natural := 0;  -- ADDED
    constant my_axi_in : axi_in_type := (wid => (others => '0'), 
                                         wdata => (others => '0'),
                                         wstrb => (others => '0'),
                                         wlast =>'0', 
                                         wvalid => '0');

begin
    comp: 
        my_component
            generic map (
                NB_AXI_SLV => NB_AXI_SLV
            )
            port map (
                axi_in_ports => (others => my_axi_in),
                axi_out_ports => open
            );

end architecture;

architecture fum of my_component_tb is

    component my_component is
        generic (
            NB_AXI_SLV: in integer range 0 to 8 := 1

        );
        port (
            axi_in_ports: in axi_in_vector(NB_AXI_SLV-1 downto 0);
            axi_out_ports: out axi_out_vector(NB_AXI_SLV-1 downto 0)
        );
    end component;
    -- constant NB_AXI_SLV:    natural := 0;  -- ADDED

    constant my_axi_in : axi_in_type := (wid => (others => '0'),
                                         wdata => (others => '0'),
                                         wstrb => (others => '0'),
                                         wlast =>'0',
                                         wvalid => '0');

    constant axi_in_vector_local: axi_in_vector(NB_AXI_SLV-1 downto 0) :=
                            (others => my_axi_in);
    signal axi_in_left: axi_in_type;

begin
    comp: 
        my_component
            generic map (
                NB_AXI_SLV => NB_AXI_SLV  -- WAS 0
            )
            port map (
                axi_in_ports => (others => my_axi_in),
                axi_out_ports => open
            );
WHEN_NOT_NULL:
    if NB_AXI_SLV > 0 generate
        axi_in_left <= axi_in_vector_local(axi_in_vector_local'left);
    end generate;                   
end architecture;

This example works because the expression in the if generate statement condition is globally static (depends on a generic) and isn't evaluated until elaboration. There are code structure implications for the analyzed design unit's object file.

This appears to be compatible with a current Synplify Pro (there are no prohibitions against top level generics apparent in the linked reference manual, caveat emptor). And about here you can see how valuable support for design top level generics can be.

IF you still have a problem provide an actual Minimal, Complete and Verifiable example particularly showing any error messages including location in the code and/or synthesis process.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.