hlslib icon indicating copy to clipboard operation
hlslib copied to clipboard

Add support for free-running functions in simulation

Open quetric opened this issue 4 years ago • 7 comments

Xilinx HLS IP can be free-running (they self-restart) for example by setting control mode to ap_ctrl_none in Vivado/Vitis HLS. This is a relatively common design pattern.

I've added an additional simulation macro, HLSLIB_FREERUNNING_FUNCTION, which wraps the function in a while(1) before launching the thread, such that the function executes repeatedly, emulating the expected real-world behaviour. This works, with a minimal application example such as this:

#include "Stream.h"
#include "Simulation.h"
#include <iostream>
#include <chrono>
#include <thread>

using namespace std;
using namespace hlslib;

void f1(Stream<int> &s, int val){
    cout << "Putting stuff in stream" << endl;
    s.Push(val);
}

void f2(Stream<int> &in, Stream<int> &out){
    cout << "Copying stuff in stream" << endl;
    out.Push(in.Pop());
}

void f3(Stream<int> &s){
    cout << "Getting stuff from stream" << endl;
    int val = s.Pop();
    cout << val << endl;
}

int main(){
    Stream<int> s0, s1;

    // Dataflow functions running in parallel
    HLSLIB_DATAFLOW_INIT();
    HLSLIB_DATAFLOW_FUNCTION(f1, s0, 2);
    HLSLIB_FREERUNNING_FUNCTION(f2, s0, s1);
    HLSLIB_DATAFLOW_FUNCTION(f3, s1);
    HLSLIB_DATAFLOW_FINALIZE();
}

There is a fundamental problem to this approach though which is that we can't expect free-running functions to ever finish. Even if I were to provide a mechanism to interrupt the restarting loop, there isn't any guarantee that the function had not been already restarted by the time the interrupt signal comes, in which case it may be blocking on a stream read. Therefore, I've chosen the simplest approach which is to join only dataflow function threads, which are kept in a separate queue from free-running function threads, which will be killed on program exit.

quetric avatar Nov 11 '21 09:11 quetric

I'm currently thinking about using some free-running kernels as well, and this seems like it could be helpful.

derpda avatar Dec 15 '21 06:12 derpda

Sorry I dropped the ball on this -- Lucian and I discussed via email, and could not come up with any perfect way to do this. The solution that Lucian proposed probably works in most cases, as the detached freerunning threads will just be killed when the program exists, but it's not very clean. If the program keeps running after the kernel launch, these functions will just sit around. Worse yet, if the kernel is called many times, each launch will create new threads that will run forever.

Ideas are welcome!

definelicht avatar Dec 15 '21 15:12 definelicht

This may be off-topic, but what does HLSLIB_FREERUNNING_FUNCTION do during synthesis? It seems to do the same as HLSLIB_DATAFLOW_FUNCTION. In my case the IP is part of the platform (an FFT module), so this probably wouldn't be my use case...

Even more off-topic (sorry, hard to find good info elsewhere): Do you have experience with using the HLS IP Libraries? I've been trying to use the FFT, but so far am unable to generate working hardware (software works). This is the "free-running function" I am trying to use.

derpda avatar Dec 16 '21 01:12 derpda

The important difference in Lucian's PR is to not wait for the freerunning functions before exiting the dataflow sections, as they will never terminate. @quetric I was actually only familiar with free-running functions as kernels, not as dataflow functions -- do they need to have the while (1) in both simulation and synthesis? Could we even augment this into the macro?

I've never used the HLS IP libraries, so I can't help you there :-)

definelicht avatar Dec 16 '21 09:12 definelicht

Freerunning functions are entire kernels, yes, and the free-running aspect is obtained by either instructing HLS to use ap_ctrl_none as a control protocol or by using s_axilite control and manually setting the autorestart bit in the registers. Otherwise the code looks the same as a non-freerunning function.

My intention with HLSLIB_FREERUNNING_FUNCTION was to enable simulation of block designs made up of kernels, some of which may be freerunning. I don't think free-running functions can exist inside a dataflow region of a larger HLS kernel, in my experience the blocks are always controlled by the generated state machine to implement a specific sequence of operation.

I don't have experience with the HLS IP libs either.

quetric avatar Dec 16 '21 11:12 quetric

I see, then HLSLIB_FREERUNNING_FUNCTION(f) should maybe resolve to while (1) { f(); }?

definelicht avatar Dec 16 '21 13:12 definelicht

That's essentially what happens, but in a thread.

quetric avatar Dec 20 '21 10:12 quetric