Intel® High Level Synthesis Compiler Standard Edition: Best Practices Guide

ID 683259
Date 12/18/2019
Document Table of Contents

7.1. Component Fails Only In Cosimulation

Discrepancies between the results of compiling your component in emulation (-march=x86-64) mode or cosimulation (-march=FPGA_name_or_part_no ) mode are typically caused by bugs in your component or testbench. However, there are some common cases where the discrepancies are cause by something other than a bug.

Comparing Floating Point Results

Use an epsilon when comparing floating point value results in the testbench. Floating points results from the RTL hardware are different from the x86 emulation flow.

Using #pragma ivdep to Ignore Memory Dependencies

The #pragma ivdep compiler pragma can cause functional incorrectness in your component if your component has a memory dependency that you attempted to ignore with the pragma. You can try to use the safelen modifier to control how many memory accesses that you can permit before a memory dependency occurs.

See Loop-Carried Dependencies (ivdep Pragma) in Intel® High Level Synthesis Compiler Standard Edition Reference Manual for a description of this pragma.

To see an example of using the ivdep pragma, review the tutorial in <quartus_installdir>/hls/examples/tutorials/best_practices/loop_memory_dependency.

Unintentional Integer Promotion

The Intel® HLS Compiler Standard Edition does not automatically promote small data types (such as unsigned char or short) to 32-bit widths during cosimulation. Other compilers (like g++) might promote integers when compiling your component for emulation. This difference in integer promotion behavior means that a value in your component has preserved overflow bits during emulation but is truncated during cosimulation.

For example, the operands in the following code example are of type unsigned char, so by default the Intel® HLS Compiler compiles the code to perform the operation with 8-bit arithmetic. Compilers that apply integer promotion rules promote the operation to use 32-bit arithmetic.
int add_width(unsigned char a, unsigned char b) {
  int sum = a + b;
  return sum;

This code example generates different overflow behavior in emulation and cosimulation. The Intel® HLS Compiler truncates the integers at 8 bit, while other C++ compilers preserve the overflow bits.

You can mimic the integer promotion behavior by using the --promote-integers compiler option. See Compiler Options in Intel® High Level Synthesis Compiler Standard Edition Reference Manual for a description of this compiler option.

To see an example of using the --promote-integers compiler option, review the tutorial in <quartus_installdir>/hls/examples/tutorials/best_practices/integer_promotion.

Check for Uninitialized Variables

Many coding practices can result in behavior that is undefined by the C++ specification. Sometimes this undefined behavior works as expected in emulation, but not in cosimulation.

A common example of this situation occurs when your design reads from uninitialized variables, especially uninitialized struct variables.

Check your code for uninitialized values with the -Wuninitialized compiler flag, or debug your emulation testbench with the valgrind debugging tool. The -Wuninitialized compiler flag does not show uninitialized struct variables.

You can also check for misbehaving variables by using one or more stream interfaces as debug streams. You can add one or more ihc::stream_out interfaces to your component to have the component write out its internal state variables as it executes. By comparing the output of the emulation flow and the cosimulation flow, you can see where the RTL behavior diverges from the emulator behavior.

Non-blocking Stream Accesses

The emulation model of tryRead() is not cycle-accurate, so the behavior of tryRead() might differ between emulation and co-simulation.

If you have a non-blocking stream access (for example, tryRead()) from a stream with a FIFO (that is, the ihc::depth<> template parameter), then the first few iterations of tryRead() might return false in co-simulation, but return true in emulation.

In this case, invoke your component a few extra times from the testbench to guarantee that it consumes all data in the stream. These extra invocations should not cause functional problems because tryRead() returns false.