LPM DIVIDE behaves differently between simulation and implementation
I have implemented a divider in a custom VHDL module in order to replace the instances of the LPM_DIVIDE IPs in my project. I've then checked the functionality of my module against the LPM_DIVIDE and verified through a simulation that, with the exception of a different initial latency, my divider behaves exactly the same way of the LPM_DIVIDE IP, covering all the dynamic of the input operands. The problem is that when I test it on the hardware, I have some slightly different results. Debugging the problem with SignalTap, I verified that the problem was due to a different behavior of the LPM_DIVIDE IP on the hardware with respect to the simulation. I verified that the different behavior occurs when the result of the division has a negative sign. I have attached the results of the simulation and of the acquisition on SignalTap in case of a division between a negative numerator and a positive denominator. The correct result should be the one of the simulation (0xFF..FFF4FF), but for a reason that I still don't understand, in the hardware implementation the result is 0xFF..FFF4FE. Do you have any suggestion? Is there some kind of rounding in the implementation that is not present in the simulation model?
However for simulation, i use your testbench and modelsim but i can't get the same result as you check attached pic. It keeps the same at FFFFFFEC78000000. Is there anything i miss?
Please ignore the previous post. I had found the root cause.
Please use the LPM_DIVIDE megafunction from IP Catalog instead of direct using the component because there're some parameters you set not properly. For example.
Denominator width can't be set to 39 if check the dropdown screenshot:
Pipeline can't be set more than numerator width screenshot:
After using the LPM_DIVIDE megafunction from IP Catalog, I get the correct simulation result screenshot:
I think that the problem is not the number of pipeline stages nor the size of the denominator (even if both the limitations are not clear for me), but following your suggestion I've tried the IP catalog, instead of instantiating directly the IP in the code. I've noticed that at a certain point of the personalization GUI of the IP, there are two radio boxes which allows the user to select the sign of the remainder:
Depending on the selection, this changes the value of the generic parameter "LPM_HINT" of the LPM_DIVIDE:
lpm_hint => "LPM_REMAINDERPOSITIVE=FALSE", when No has been selected
lpm_hint => "LPM_REMAINDERPOSITIVE=TRUE", when Yes has been selected
Setting the generic LPM_HINT => "LPM_REMAINDERPOSITIVE=TRUE" in the code, even the lpm_divide_self_operands_gen.vhd returns the same result of signal_tap.
In the lpm_divide_self_operands_gen.vhd i posted previously, the parameter was set to "UNUSED". Checking here: https://www.intel.com/content/www/us/en/docs/programmable/683490/24-1/parameters-72926.html , the parameter was not required, and in any case I get the parameter value from the component declaration in LPM_PACK.vhd in the <Quartus® Primeinstallation directory>\libraries\vhdl\lpm directory, by default is set to "UNUSED".
I see 2 issues here:
Leaving the parameter LPM_HINT => "UNUSED" causes a mismatch between the simulation and the implementation, because the behaviour of the IP in simulation is like the parameter is set to FALSE, while in implementation the remainder sign is always positive, therefore like the parameter is set to TRUE. Therefore the parameter shall be set to a value and not leaved "UNUSED", or somehow Quartus should alert the user for this mismatch!
Even setting LPM_REMAINDERPOSITIVE = TRUE, the quotient is not correct, since, for example, if you try a simple division like -5/3, the result should be -1 with a remainder of -2, while the IP returns -2 with a remainder of 1. That's obviously not correct, and I didn't expect that changing that parameter changes also the values of quotient and remainder, but only the sign of the remainder, as the name of the parameter and the explanation provided in the guide states.
For confirmation, I attached a more simple version of the lpm_divide_self_operands_gen.vhd, where I instantiate 3 version of the LPM_DIVIDE function with only 1 stage of pipeline, as you suggested and with the parameter LPM_HINT set to "LPM_REMAINDERPOSITIVE=TRUE", "LPM_REMAINDERPOSITIVE=FALSE" and "UNUSED". I also update the lpm_divide_self_operands_gen_tb.vhd setting the denominator width to 32 (it is an allowed value). The operators are now simply -5 and 3. If you simulate the module you'll get that the outputs are the following:
So we get 2 different quotient and remainder values, not only the sign of the remainder changes setting the LPM_HINT parameter. The s_quotient_hint_rem_pos_true value is not correct and is the value that we have in HW leaving the parameter "UNUSED", that's different from the value that we have in simulation. On signaltap (I also attached the updated Quartus Project), we see:
Yes, when tick Yes the LPM_HINT will use "LPM_REMAINDERPOSITIVE=TRUE" while tick No the LPM_HINT will use "LPM_REMAINDERPOSITIVE=FALSE". If LPM_HINT uses either "LPM_REMAINDERPOSITIVE=TRUE" or "LPM_REMAINDERPOSITIVE=FALSE", the results between simulation and signal tap are same.
If LPM_HINT use "UNUSED", the results between simulation and signal tap are not same. Please don't use "UNUSED" because correct usage are "LPM_REMAINDERPOSITIVE=TRUE" or "LPM_REMAINDERPOSITIVE=FALSE" for LPM_HINT.
So we can conclude the LPM DIVIDE behaves same between simulation and signal tap right? which answer the title of this thread.
So there's another new question regarding functionality right?
For functionality in this post community.intel.com/t5/Intel-Quartus-Prime-Software/LPM-DIVIDE-behaves-differently-between-simulation-and/m-p/1672473#M85726, i think should be no problem check below:
For a numerator of −5 and a denominator of 3, using the LPM (Library of Parameterized Modules) IP division method where the remainder is always positive, we follow these rules:
Quotient Calculation: Perform integer division (truncate towards zero):
−5÷3=−1(integer division)
Remainder Calculation:
remainder=numerator−(quotient×denominator)
remainder=−5−(−1×3)=−5+3=−2
However, since LPM IP requires a positive remainder, we adjust by adding 3 to remainder and reducing quotient by 1:
new quotient=−1−1=−2
new remainder=−2+3=1
Final Result:
LPM_HINT => "LPM_REMAINDERPOSITIVE=TRUE":
quotient=-2
remander=1
LPM_HINT => "LPM_REMAINDERPOSITIVE=FALSE":
quotient=-1
remander=-2
The simulation and signal tap result for LPM_HINT => "LPM_REMAINDERPOSITIVE=TRUE" and LPM_HINT => "LPM_REMAINDERPOSITIVE=FALSE" are correct.
Only the result for LPM_HINT => "UNUSED" is incorrect, please don't use LPM_HINT => "UNUSED" (Incorrect Usage)
thank you very much for the detailed information about the IP’s functionality. However, in my opinion, the issue of the mismatch between simulation and implementation cannot be considered resolved, as I would like to understand:
Where is it stated in Altera's documentation that using the parameter LPM_HINT => "UNUSED" is incorrect, as you mentioned?
If LPM_HINT => "UNUSED" is incorrect, why is it assigned as the default value in the module declaration inside lpm_pack.vhd?
To truly resolve the issue and prevent it from occurring in the future for other users, I believe that the simulation model of LPM_DIVIDE should be updated so that the behavior in simulation matches the implemented behavior, regardless of the value of the LPM_HINT parameter.
The LPM DIVIDE function in Intel FPGAs may behave differently between simulation and implementation due to synthesis optimizations, timing differences, and hardware constraints. Simulation tools often assume ideal conditions, while actual FPGA hardware introduces latency and resource limitations. The division operation might be pipelined in implementation, causing additional clock cycle delays. Mismatches in reset handling or clock domains can also lead to unexpected results. To ensure consistency, verify timing constraints, pipeline settings, and resource utilization in Intel Quartus Prime. Running post-implementation simulations can help detect and resolve discrepancies.