Make Cosim Great Again

Vivado HLS is a handy(?) high-level synthesis (HLS) tool that supports C++11 features. However, its C-RTL co-simulation (cosim) is broken in the sense that it doesn’t fully support C++11. The cosim compiler seems to be stuck at g++ 4.6.3 forever. This makes it impossible to make fancy stuff like Google Test work as part of the testbench with the cosim compiler.

Let’s recap the situation:

  • We are talking about the Linux toolchains.
  • The cosim framework is designed to be started via a TCL command and it is hard to invoke it manually.
  • The cosim framework requires that the testbench contains a main function so you cannot make the cosim executable a library and link it with other programs.
  • The cosim compiler is a customized version of g++ 4.6.3 and cannot be upgraded or replaced. Note that although clang is provided as an alternative, it is very tricky to enable C++11, and also seems to be stuck at an old version as well.
  • Although some ldflags work, I never got the cosim compiler correctly link a library against the source code.

山重水复疑无路,柳暗花明又一村。————陆游

When there’s a will, there’s a way — the ABI is still compatible, isn’t it? You can compile whatever program with whichever compiler, as long as it can be loaded as a shared object by the cosim executable. So the idea is clear:

  • Compile the fancy part of the code into a shared object with whichever compiler you want (just keep the ABI compatible). To create a shared object with g++ or clang++, add -fPIC when compiling and add -shared when linking.
  • Make the cosim executable load the shared object at runtime. This can be done with something like this
    #include 
    // This function pointer will be used to invoke the function in the shared object.
    void (*entry_function)();
    void* handle = dlopen("foo.so", RTLD_LAZY);
    if (handle == nullptr) {
      clog << dlerror() << endl;
      exit(EXIT_FAILURE);
    }
    entry_function = reinterpret_cast<void(*)()>(dlsym(handle, "EntryFunction"));
    char *error;
    if ((error = dlerror()) != nullptr)  {
      clog << dlerror() << endl;
      exit(EXIT_FAILURE);
    }
    (*entry_function)();
    // Don't dlclose(handle) or cosim won't work.</void(*)()>
  • If the function invoked in the shared object needs to invoke the hardware module, put the top-level function in a wrapper instead of invoking it directly. This is because cosim is actually doing a source-to-source transformation to the testbench and invoking a top-level module directly will only run C simulation. Note that you need to export the symbols in the testbench by adding -Wl,--export-dynamic to the ldflags of the cosim_design TCL command to invoke functions in the testbench from the shared object.
  • Compile the antique part of the code with the cosim compiler. Add -ldl to the ldflags of the cosim_design TCL command to enable dynamic library loading.

To use clang and enable full C++11 support, you need to add -compiler clang -mflags "DFLAG=-D_GLIBCXX_USE_CXX11_ABI=0" to the cosim_design TCL command.

I managed to get cosim working with Google Test!


Update: turns out that waveform can be dumped from SDAccel cosim flow! Just set launch_waveform to batch in sdaccel.ini. Refer to the SDAccel command line flow manual for more information about how to use sdaccel.ini.


References

  1. Vivado HLS User Guide
  2. Shared library calling a function in its loader
  3. SDAccel Command Line Flow