Programmable Devices
CPLDs, FPGAs, SoC FPGAs, Configuration, and Transceivers
20693 Discussions

VHDL Numeric Type Conversion

Altera_Forum
Honored Contributor II
1,956 Views

I am using a bunch of variables to do some math. These variables, X and Y coordinates, are converted to different numeric types for ease of math operations. The outputs, Xm and Ym, are SLV's and depend on the intermediary variables Xc and Yc. Xm and Ym are simulated correctly using Quartus II 9.1 native simulator, but the intermediary variables Xp and Yp, who also depend on Xc and Yc, are not. How could this be? Here are the relevant snipets of my code: 

--- 

use ieee.numeric_std.all; 

... 

generic(dst_max : positive := 300000); 

... 

Xm, Ym : out std_logic_vector(19 downto 0); 

... 

variable d1, d2, d3, d4 : natural range 0 to dst_max; 

variable Xc, Yc, Xc1, Yc1, Xp, Yp : integer range -dst_max to dst_max; 

variable Xc_sv, Yc_sv, Xc_sv1, Yc_sv1 : signed(19 downto 0); 

... 

elsif (d2 /= 0) and (d4 /= 0) then 

Xc1 := (d4 - d2); 

Xc_sv1 := to_signed(Xc1, 20); 

Xc_sv := Xc_sv1(19) & Xc_sv1(19 downto 1); -- shift right by 1 to divide by 2 

Xc := to_integer(Xc_sv); 

else 

Xc := Xp; -- use previous 

end if; 

... 

elsif (d1 /= 0) and (d3 /= 0) then 

Yc1 := (d3 - d1); 

Yc_sv1 := to_signed(Yc1, 20); 

Yc_sv := Yc_sv1(19) & Yc_sv1(19 downto 1); -- shift right by 1 to divide by 2 

Yc := to_integer(Yc_sv); 

else 

Yc := Yp; 

end if; 

Xp := Xc; -- update previous 

Yp := Yc; -- Xp and Yp DO NOT SIMULATE CORRECTLY 

Xm <= std_logic_vector(to_signed(-Xc, 20)); -- move 

Ym <= std_logic_vector(to_signed(-Yc, 20)); -- Xm and Ym SIMULATE CORRECTLY 

--- 

Simulation Results: 

d1 = 1955, d2 = 300000, d3 = 158100, d4 = 23456 

Xp should be -138272, but is 430016 

Yp should be 78072, but is -369448 

Xm is 138272 (correct) 

Ym is -78072 (correct) 

Xc* and Yc* could not be displayed by the simulator
0 Kudos
12 Replies
Altera_Forum
Honored Contributor II
594 Views

There is nothing here to show a problem. Are you sure the problem isnt elsewhere? 

 

Out of interest, why do you do so many conversions? If this is not the top level, why not just leave all the ports as integers? 

 

Also. If you want to divide by 2, do a divide by 2. The synthesisor will recognise it as a right shift: 

 

Yx_sv <= Yx_sc1/2;
0 Kudos
Altera_Forum
Honored Contributor II
594 Views

I didn't calculate the expectable Xp result, but I think, it can't be -138272. In the expression Xc1 := (d4 - d2), d4 and d2 are both of the natural type, so (d4-d2) causes an overflow but doesn't have a negative result.

0 Kudos
Altera_Forum
Honored Contributor II
594 Views

 

--- Quote Start ---  

I didn't calculate the expectable Xp result, but I think, it can't be -138272. In the expression Xc1 := (d4 - d2), d4 and d2 are both of the natural type, so (d4-d2) causes an overflow but doesn't have a negative result. 

--- Quote End ---  

 

 

because natural is a subtype of integer, (d4-d2) is actually an integer. its only when it's assigned is range checking done, so can go negative.
0 Kudos
Altera_Forum
Honored Contributor II
594 Views

 

--- Quote Start ---  

because natural is a subtype of integer, (d4-d2) is actually an integer. its only when it's assigned is range checking done, so can go negative. 

--- Quote End ---  

 

So you mean, (d4-d2) will get the correct numeric value? I'm not sure from which type property this could be derived. Actually, integer types are converted to signed and unsigned before synthesis, so I fear, the subtype doesn't mean anything here. Furthermore, the bit width is increased by one in the assignment. 

 

P.S.: Perhaps more important, it's impossible to guess how the code is synthesized from the shown snippets. The various variables used in the design, including d1 to d4 aren't real design objects unless they convert to registers or LE outputs.
0 Kudos
Altera_Forum
Honored Contributor II
594 Views

 

--- Quote Start ---  

So you mean, (d4-d2) will get the correct numeric value? I'm not sure from which type property this could be derived. Actually, integer types are converted to signed and unsigned before synthesis, so I fear, the subtype doesn't mean anything here. Furthermore, the bit width is increased by one in the assignment. 

 

--- Quote End ---  

 

 

I was refering the RTL level simulation, rather than post synthesis. But yes, Quartus may keep d4 and d2 as an unsigned, and so when the subtraction occurs it may underflow before asignment into the signed variable. 

 

The safest way around this may be to keep everything in the same type (integer or signed), rather than constant changing from natural (which quartus may treat as an unsigned when converted to bits) to signed.
0 Kudos
Altera_Forum
Honored Contributor II
594 Views

 

--- Quote Start ---  

There is nothing here to show a problem. Are you sure the problem isnt elsewhere? 

Out of interest, why do you do so many conversions? If this is not the top level, why not just leave all the ports as integers? 

Also. If you want to divide by 2, do a divide by 2. The synthesisor will recognise it as a right shift: 

Yx_sv <= Yx_sc1/2; 

--- Quote End ---  

 

The problem is that Xp and Yp are not simulated as expected, while Xm and Ym are. Both are derived from Xc and Yc as my previous code snippet shows: 

--- 

Xp := Xc; -- update previous 

Yp := Yc; -- Xp and Yp DO NOT SIMULATE CORRECTLY 

Xm <= std_logic_vector(to_signed(-Xc, 20)); -- move 

Ym <= std_logic_vector(to_signed(-Yc, 20)); -- Xm and Ym SIMULATE CORRECTLY 

--- 

The previous coordinates Xp and Yp are needed if the current coordinates Xc and Yc are invalid. 

This entity is not the top level, but Xm and Ym will be output pins at the top level. If I have them as output integers at this level, how do I read them to convert them to SLV's at the top level? Since you can't read outputs, can I have them as buffer integers at this level? 

The synthesizer really will recognize the divide by 2 as a right shift? Is this true for all powers of 2? I thought division was a very costly operation. 

Here's the simple math I wanted to implement: 

Xc = (d4-d2)/2 = (23456-300000)/2 = (-276544)/2 = -138272 

Xp = Xc = -138272 

Xm = -Xc = 138272 

I subtyped and range-limited my variables to reduce the bits/LE's used for synthesis.
0 Kudos
Altera_Forum
Honored Contributor II
594 Views

If it is a constant 2^n, then it should recognise it as a shift register. 

 

As for why it is not working, are you doing a RTL or post-synthesis simulation?
0 Kudos
Altera_Forum
Honored Contributor II
594 Views

I am doing a post-synthesis timing simulation using Quartus 9.1 native simulator. 

I changed my d# variables from type natural to (unsigned) integer. They represent distances, so cannot be negative. I got rid of the intermediary variables Xc_* and Yc_*, but kept Xm and Ym as output SLVs. But I still get the same results: 

d1 = 1955, d2 = 300000, d3 = 158100, d4 = 23456 

Xp should be -138272, but is 430016 

Yp should be 78072, but is -369448 

Xm is 138272 (correct) 

Ym is -78072 (correct) 

Xc* and Yc* could not be displayed by the simulator 

Here's my revised code snippet: 

--- 

generic(dst_max : positive := 300000); 

... 

Xm, Ym : out std_logic_vector(19 downto 0); 

... 

variable d1, d2, d3, d4 : integer range 0 to dst_max; 

variable Xc, Yc, Xc1, Yc1, Xp, Yp : integer range -dst_max to dst_max; 

... 

Xc1 := (d4 - d2); 

Xc := Xc1/2; 

... 

Yc1 := (d3 - d1); 

Yc := Yc1/2; 

... 

Xp := Xc; -- update previous center 

Yp := Yc; 

Xm <= std_logic_vector(to_signed(-Xc, 20)); -- move back to center 

Ym <= std_logic_vector(to_signed(-Yc, 20)); 

--- 

I don't understand how Xp and Yp have the unexpected simulation values while Xm and Ym have the correct simulation values, when both are representations of Xc and Yc.
0 Kudos
Altera_Forum
Honored Contributor II
594 Views

 

--- Quote Start ---  

Xc* and Yc* could not be displayed by the simulator 

--- Quote End ---  

 

Yes, because they are variables. You can use signals to make them visible. 

 

My previous comment about d1 to d4 is still valid, I fear. 

 

--- Quote Start ---  

Perhaps more important, it's impossible to guess how the code is synthesized from the shown snippets. The various variables used in the design, including d1 to d4 aren't real design objects unless they convert to registers or LE outputs. 

--- Quote End ---  

 

To ask explicitely: how do you load the values for d1 to d4? 

 

 

--- Quote Start ---  

I changed my d# variables from type natural to (unsigned) integer. They represent distances, so cannot be negative. 

--- Quote End ---  

 

May be, but you possibly don't understand the side-effects of implicite type conversion during assignment. I think, this previous suggestion is quite reasonable. 

 

--- Quote Start ---  

The safest way around this may be to keep everything in the same type 

--- Quote End ---  

0 Kudos
Altera_Forum
Honored Contributor II
594 Views

 

--- Quote Start ---  

Yes, because they are variables. You can use signals to make them visible. 

My previous comment about d1 to d4 is still valid, I fear. 

To ask explicitely: how do you load the values for d1 to d4? 

May be, but you possibly don't understand the side-effects of implicite type conversion during assignment. I think, this previous suggestion is quite reasonable. 

--- Quote End ---  

 

I do know the difference b/t variables and signals. I wanted to use variables because they don't represent HW and I don't have to wait for the clock to change them. I did try using signals before posting this thread, and got even more unexpected simulation results. But I haven't tried using signals since changing them all to integers. I'll give this a try. 

Here's how I load the values for d1 to d4: 

--- 

dst1, dst2, dst3, dst4 : in integer range 0 to dst_max; 

valid1, valid2, valid3, valid4 : in std_logic; 

... 

if valid1 = '1' then 

d1 := dst1; 

-- same for d2, d3, d4 

--- 

I already stated that I changed all my variables to type integer. So the only type conversion I am doing is for the outputs Xm and Ym: 

Xm <= std_logic_vector(to_signed(-Xc, 20)); 

There are no type conversions for Xp and Yp, but they are incorrect. If there's a problem w/ d1 to d4, why are Xm and Ym correct, though I had to type convert Xc and Yc?
0 Kudos
Altera_Forum
Honored Contributor II
594 Views

the fact you are doing post synthesis simulation probably explains why they are not working. Quartus is probably converting everything to a signed number during synthesis, or you are showing them in the simulator as signed when actually they are unsigned. 

 

 

--- Quote Start ---  

 

I do know the difference b/t variables and signals. I wanted to use variables because they don't represent HW and I don't have to wait for the clock to change them. 

 

--- Quote End ---  

 

 

This shows you clearly do not understand variables. Just because they are updated immediatly does not mean they do not represent hardware. At the very least, variables may represent a wire between one register to the next. And in some circumstances they can be used to instantiate anything a signal can. 

 

Consider the following two bits of code. The first one has a single register between the input and output. 

 

process(clk) variable v : std_logic; begin if rising_edge(clk) then v := input; output <= v; end if; end process;  

 

Now this code: 

 

process(clk) variable v : std_logic; begin if rising_edge(clk) then output <= v; v := input; end if; end process;  

 

Now v represents an additional register in the pipeline, so there are 2 registers between the input and output instead of just 1.
0 Kudos
Altera_Forum
Honored Contributor II
594 Views

The problem with variables is, that they can represent hardware, as Tricky has shown, but they can be also purely virtual, removed during synthesis. This won't be a problem normally, but if arithmetic overflows and implicite type conversions "happen" to the data, the result may be difficult to predict. 

 

 

--- Quote Start ---  

I already stated that I changed all my variables to type integer. So the only type conversion I am doing is for the outputs Xm and Ym. 

--- Quote End ---  

 

 

As already mentioned, I see a possible overflow and an implicite type conversion in Xc1 := (d4 - d2). The input is unsigned integer and the output signed integer. As previously discussed, there's a possible ambiguousity, because the integer subtypes are synthesized as signed and unsigned by the compiler. So the synthesized result may be different from expectable integer behaviour or ModelSim simulation.  

 

What to do? You can either complain about possible incorrect synthesis of integer subtypes by Quartus, or try to avoid these ambiguousities by a clearer typing.
0 Kudos
Reply