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

ID 683152
Date 10/04/2021
Public

A newer version of this document is available. Customers should click here to go to the newest version.

Document Table of Contents

10.1. Component Fails Only In Simulation

Discrepancies between the results of compiling your component in emulation (-march=x86-64) mode or simulation (-march=FPGA_name_or_part_no ) mode are typically caused by relying on undefined or implementation-defined behavior in your component or testbench. However, there are some common cases where the discrepancies are caused by something else.

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 Pro 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.

Check for Uninitialized Variables

Many coding practices can result in behavior that is undefined by the C++ specification. Sometimes this undefined behavior works one way in emulation and a different way in simulation.

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 simulation 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 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 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.

Invoking Component With ihc_hls_enqueue() Or ihc_hls_enqueue_noret()

If you invoke your component with ihc_hls_enqueue() Or ihc_hls_enqueue_noret(), ensure that you are also calling ihc_hls_component_run_all().

If you do not call ihc_hls_component_run_all(), simulation of your component fails.