Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for free-running functions in simulation #53

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

quetric
Copy link

@quetric quetric commented Nov 11, 2021

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.

@derpda
Copy link
Contributor

derpda commented Dec 15, 2021

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

@definelicht
Copy link
Owner

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!

@derpda
Copy link
Contributor

derpda commented Dec 16, 2021

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.

@definelicht
Copy link
Owner

definelicht commented Dec 16, 2021

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 :-)

@quetric
Copy link
Author

quetric commented Dec 16, 2021

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.

@definelicht
Copy link
Owner

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

@quetric
Copy link
Author

quetric commented Dec 20, 2021

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants