Variables in VHDL conversion


#1

Consider the following MyHDL code:

@always_comb
def add():
    bx_sum = bx[0].signed() + bx[1].signed() + bx[2].signed()
    ay_sum = ay[0].signed() + ay[1].signed()
    result.next = (bx_sum << BX_SHIFT) - (ay_sum << AY_SHIFT)

It converts to this piece of VHDL:

PREFIX_ADD: process (bx, ay) is
    variable bx_sum: integer;
    variable ay_sum: integer;
begin
    bx_sum := to_integer((bx(0) + bx(1)) + bx(2));
    ay_sum := to_integer(ay(0) + ay(1));
    result <= to_signed(shift_left(bx_sum, 12) - shift_left(ay_sum, 0), 67);
end process PREFIX_ADD;

This won’t compile, because the compiler can’t determine whether the first argument of shift_left() is signed or unsigned. I think this is a bug in the VHDL conversion. To fix this, let’s try moving the shift to where the compiler can still infer the type:

@always_comb
def add():
    bx_sum = bx[0].signed() + bx[1].signed() + bx[2].signed() << BX_SHIFT
    ay_sum = ay[0].signed() + ay[1].signed() << AY_SHIFT
    result.next = bx_sum - ay_sum

Now we get this:

PREFIX_ADD: process (bx, ay) is
    variable ay_sum: integer;
    variable bx_sum: integer;
begin
    bx_sum := to_integer(shift_left(((bx(0) + bx(1)) + bx(2)), 12));
    ay_sum := to_integer(shift_left((ay(0) + ay(1)), 0));
    result <= to_signed(bx_sum - ay_sum, 67);
end process PREFIX_ADD;

This compiles, but is it safe? Do vendor tools support arbitrarily large integer variables? Should I do this instead:

@always_comb
def add():
    result.next = (
        (bx[0].signed() + bx[1].signed() + bx[2].signed() << BX_SHIFT) -
        (ay[0].signed() + ay[1].signed() << AY_SHIFT)
    )

This gives the following VHDL, which compiles and should definitely be safe:

result <= (shift_left(((bx(0) + bx(1)) + bx(2)), 12) - shift_left((ay(0) + ay(1)), 0));

However, I think the first version (the one that doesn’t compile now) is the most readable.


#2

What about the sign of bx_sum and ay_sum after the shift (independently of MyHDL/VHDL) ?


#3

What about it? Both bx_sum and ay_sum are signed integers. Shifting doesn’t affect the sign. Is that what you meant?


#4

That’s it.
“1010110” is negative.
“1010110” << 1 = “0101100” This is positive.


#5

In Python, shifting doesn’t affect sign. The same is true for shifting signed types in VHDL. So as far as I know, “1010110” << 1 ≠ “0101100”.

Edit: I missed your point about the shifted value not being wider than the original, like it is in Python. As far as I know, this is the case also in VHDL, so the first bit would not be dropped.

Edit: Except I’m wrong:

function SHIFT_LEFT (ARG: SIGNED; COUNT: NATURAL) return SIGNED;
– Result subtype: SIGNED(ARG’LENGTH-1 downto 0)
– Result: Performs a shift-left on a SIGNED vector COUNT times.
– The vacated positions are filled with ‘0’.
The COUNT leftmost elements are lost.

So I actually want to concatenate, not shift.


#6

Don’t forget that you can simulate code that is not convertible to VHDL/verilog. This is usefull but you must be careful about it.


#7

That’s it :wink:

(Have to type more than 20 char, so lets go…)


#8

Except that in this case it does convert. It just doesn’t do the same thing, which is rather dangerous. I will need to review my code for erroneous uses of the shift operator.


#9

This is why co-simulation exists.
I personally don’t use it. I check VHDL generated code when I have a doubt.


#10

@mhavu Perhaps declare interim Signal avoiding the inference of variables. Unfortunately MyHDL will now wrongly complain that you are reading outputs of the @always_comb process back - I commented that out in my MHDL code base :smile:
I also think that you can promote the variable by specifying it outside the @always_comb as an intbv, so the 32-bit limit of the integer (in VHDL) doesn’t bite.


#11

I can get rid of the complaint by adding one more @always_comb process. This may be the cleanest option. It will also allow me to continue using left shift the way I have been doing. (Thanks for @DrPi for spotting the error!)

That won’t work because of Python’s duck typing. The assignment will just replace whatever intbv value the variable had by a new integer value. The VHDL conversion in MyHDL is not clever enough to intervene with that. (I don’t know whether that would even be possible.)


#12

I think it works like this:

temp = intbv(0)[8:]

@always_comb
def comb():
   temp[:] = something + whatever

I almost never use variables :slight_smile:


#13

Ah, true. That should work.


#14

Should there be a .next in there?


#15

Not if temp is not a Signal. If it is, temp.next will suffice. (The [:] is not necessary.)


#16

Oh yeah, missed lack of a Signal :slight_smile:

TBH, I always use a signal explicitly. I’m very wary of variables because I keep getting burned by them inside the downstream tools.