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 <dlfcn.h>
    // 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.
  • 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!


References

  1. Vivado HLS User Guide
  2. Shared library calling a function in its loader