Search code examples
vhdl

Why does multiplication in VHDL sometimes not work as expected with an integer?


In VHDL with strong typing when doing multiplication I'd expect the following statement to end up with a 14 bit output:

frame_addr  : out STD_LOGIC_VECTOR(13 downto 0);

...

signal  y_pos       : unsigned(4 downto 0);

...

frame_addr <= std_logic_vector(y_pos * 320);

But I end up with the error:

expression has 10 elements, but must have 14 elements

Which makes NO sense to me... As 320 should be at-least 9 bits as an unsigned, 10 as an integer, and when multiplied with the 5 bit signal y_pos I'd expect to see AT-LEAST 14 bits...

I have another section of code where this works out PERFECTLY FINE:

ram_addr <= std_logic_vector(h_pos + (v_pos * 80))(14 downto 0);

The multiplication with 80 works just fine, no errors.

So I tried everything to get up to 14 bits... and of-course the multiplication just appears to NOT happen when in my logic... So I finally said, OK what if I just do multiplication with BINARY tell it exactly what I want...

frame_addr <= std_logic_vector(y_pos * "101000000");

Compiles fine with no errors... And the logic starts working as I would expect it to.

So why is there this inconsistency? Both of these are within an architecture structure for relevance, not within processes or anything like that. I just can't wrap my head around why there are these inconsistencies... Both use the same libraries:

library ieee;
use ieee.std_logic_1164.ALL;
use ieee.numeric_std.ALL;

So what gives? Do I have to use binary anywhere I want a number for any kind of code consistency?


Solution

  • Because integer has no bitwise length, it is converted to the same length as y_pos. So in your first example, Y_pos is 5 bits. so 320 gets converted to a unsigned also of 5 bits - giving a 10 bit result. This rule is the same for all of the arithmetic operators for signed and unsigned.

    Integers in VHDL have no bitwise definition, and so need to be converted somehow. The compiler cannot do intelligent sizing, so relies on the user to size it properly. In your example, 320 getting converted to 5 bits clearly truncates the value, and you would actually end up with "00000" and a warning that this is happening during simulation.

    There is no inconsistency here, "101000000" and 320 are completely different and unrelated types. Unsigned is not an integer - it is a binary representation of an unsigned integer.

    You probably want to use the to_unsigned() function on your integer literals to get an explicit size:

    frame_addr <= std_logic_vector(y_pos * to_unsigned(320, 9));