2

I'm trying to write some fairly generic VHDL code but I'm running into a situation where I don't understand the standard well enough. (I'm using VHDL-2008.)

I have written a function that operates on unconstrained std_logic_vector(s) and returns an unconstrained std_logic_vector. However, it seems as if I am unable to use this function as an input to a port in my entity if I pass two (constrained) std_logic_vectors to it (see instantiation of test_2 in my example program). However, for some reason it seems to work ok if I pass bit string literals to it (see instantiation of test_1).

Can someone explain why the I am not allowed to use the concatenate() function as an input in the instantiation of test_2 while I am allowed to use a very similar construct in the instantiation of test_1?

To try the code with ModelSim I compiled it with vcom -2008 unconstrained_example.vhd

-- test entity/architecture
library ieee;
use ieee.std_logic_1164.all;

entity test is
  port (value : in std_logic_vector);
end entity;

architecture a of test is
begin
  -- Intentionally empty
end architecture;

library ieee;
use ieee.std_logic_1164.all;

-- Test instantiation
entity testit is
end entity;
architecture a of testit is
  signal my_constrained_slv1 : std_logic_vector(5 downto 0);
  signal my_constrained_slv2 : std_logic_vector(9 downto 0);
  function concatenate(value1 : std_logic_vector; value2 : std_logic_vector) return std_logic_vector is
  begin
    return value1 & value2;
  end function;
begin
  process begin
    -- Using the function in this context seems to work ok
    report "Value is " & to_string(concatenate(my_constrained_slv1, my_constrained_slv2));
    wait;
  end process;

  -- This instantiation seems to work
  test_1: entity work.test
    port map (
      value =>  concatenate("000000", "1111111111"));

  -- For this entity instantiation I'm getting an error from ModelSim:
  -- ** Error: unconstrained_example.vhd(43): (vcom-1383) Implicit signal in port map for port "value" is not fully constrained.
  test_2: entity work.test
    port map (
      value =>  concatenate(my_constrained_slv1, my_constrained_slv2));
end architecture;
tamyrlin
  • 51
  • 8

2 Answers2

2

Your function call is not a conversion function and also does not fulfill the requirements to spare an implicit signal declaration.

VHDL-2008 allows such complex expressions in a port association. The language says, that in such cases, an implicit signal will be created:

If the actual part of a given association element for a formal signal port of a block is the reserved word inertial followed by an expression, or is an expression that is not globally static, then the given association element is equivalent to association of the port with an anonymous signal implicitly declared in the declarative region that immediately encloses the block. The signal has the same subtype as the formal signal port and is the target of an implicit concurrent signal assignment statement of the form:

anonymous <= E;

where E is the expression in the actual part of the given association element. The concurrent signal assignment statement occurs in the same statement part as the block.

Source: IEEE-1076-2017 Draft 5a

signal temp : std_logic_vector; -- derived from the formal in the port association list

temp <= concatenate(my_constrained_slv1, my_constrained_slv2);
test_2: entity work.test
  port map (
    value => temp
  );
end block;

The problem is, that VHDL needs to infer the type for the implicit signal temp from the formal in the port association list (value : std_logic_vector). It knows, that it is a std_logic_vector, but no constraints are known, due to the unconstrained port.

So if port value in entity test is constrained, it should work:

entity test is
  port (
    value : in std_logic_vector(15 downto 0)
  );
end entity;
Paebbels
  • 13,346
  • 11
  • 50
  • 114
  • You are right that it works if I define a subtype, but that solution is not very useful out-of-the-box since the whole idea of the function I'm trying to write is to avoid having to specify bit-widths in the first place. (The thing that is confusing me the most though is that bit string literals of a known width are allowed but std_logic_vectors of a known width are not allowed as inputs to this function.) – tamyrlin Dec 20 '17 at 13:55
  • It should be related to local and global staticness rules. The expression in `test_1` is of another kind then the expression of `test_2`. The first one has locally static operands and because it's a locally defined *pure* function, it should preserve locally staticness. I think in `test_1` is no intermediate implicit signal involved. I haven't searched for the matching LRM paragraphs... – Paebbels Dec 20 '17 at 16:45
2

I came up with the following workaround which is quite ugly but fulfills my main criteria that I should not have to manually specify any widths or repeat any information. By hiding away the call to concatenate in a function I can reuse the function to get the range further down. A short experiment indicates that Vivado 2015.4 accepts this construct as well.

test_2_helper : block
  impure function test_2_help_func return std_logic_vector is
  begin
    -- This is the only place I have to change in case the assignment has to
    -- change in some way. (E.g. use other variables or become more complicated, etc.)
    return concatenate(my_constrained_slv1, my_constrained_slv2);
  end function;
  signal test_2_helper_sig : std_logic_vector(test_2_help_func'range);
begin
  test_2: entity work.test
    port map (
      -- It seems to be syntactically legal to use test_2_help_func(test_2_help_func'range)
      -- here. Unfortunately this does not work in simulation. Probably because the
      -- test_2_help_func does not have any explicit arguments and this may cause issues
      -- with the event driven simulation. As a work around the test_2_helper_sig signal
      -- is assigned every clock cycle below instead.
      value =>  test_2_helper_sig);

  process
  begin
    -- Note: If you remove the wait for the clock edge and instead use process(all) 
    -- test_2_helper_sig does not seem to change during simulation, at least in
    -- Modelsim 10.6 where I tested this.
    wait until rising_edge(clk);
    test_2_helper_sig <= test_2_help_func;
  end process;
end block;

Note: This is inspired by the following answer: VHDL - Why does using the length attribute directly on a function produce a warning?

tamyrlin
  • 51
  • 8
  • I have. never imagined such a VHDL construct before ... And this is supported by Vivado? What version? – Paebbels Dec 20 '17 at 23:16
  • Can you combine your code using `'range` and a qualified expression? `value => std_logic_vector(concatenate(my_constrained_slv1, my_constrained_slv2)'range)'(concatenate(my_constrained_slv1, my_constrained_slv2))` ? A qualified expression looks like this: `type'(com and plex xor expres nor sion)`. The `type` is used to give VHDL a hint for the expected type of an expression. In my proposed example code, I use your `'range` idea to create a constrained type infront of such an expression. – Paebbels Dec 20 '17 at 23:21