Jump to content

How to make a "single-only" sc_thread wait for a notify() from external host thread


yaohe

Recommended Posts

Hi Experts,

I'm making a "polling-processing" mechanism with systemc. In my original SC env, I have two SC_THREAD in a SC module:

one is "polling_thread", which actually acts as a socket server, polls messages from socket client;

the other SC_THREAD is "processing_thread", which consumes the received messages;

To make "polling_thread" keep active, I simply use some code like:

while (1) {
  wait(10, SC_NS);
  //polling function to get message from socket
  //notify processing_thread to process the message
}

In "processing_thread", I do something like:

while (1) {
	wait(notify_ev_from_polling_thread);
	//process the message, AND THE LOGIC IS TIME CONSUMING!
}

And it works well.

 

Now, consider "processing_thread" is the user logic I only care about, for simulation accuracy and deterministic, I only expect "processing_thread" to advance SC simulation time and I can record the behavior by getting SC time, e.g. sc_get_timestamp()

For the above purpose, I'm changing "polling_thread" from a SC_THREAD into an external host thread by e.g. pthread. By following the example code of async_request_update() (

Then, there is only one SC_THREAD left in the SC kernel control which is "processing_thread", and looks like the "wait" in "processing_thread" will cause the kernel switch behavior but there is no other thread controlled by kernel. 

I expected to see "processing_thread" keeps waiting until the external thread notify it, and do such turn-around in the while loop. But in fact, sc_start() directly returned when wait() is called in "processing_thread".

I'm wondering, is it my mistake of using something or it's just the systemC mechanism. If so, is there any workaround to my requirement?

thanks!

Link to comment
Share on other sites

The SystemC kernel is not aware of 'external' events. So if it does not have any further event to process it will finish the simulation.

A solution to this might be keep your polling event but extend the logic a bit:

while (1) {
  wait(10, SC_NS);
  wait(async_event);
}

This async_event is something like the ThreadSafeEvent  in the post you referenced. This way you block the SysremC kernel until you get an external event and then let it run for 10ns.

Link to comment
Share on other sites

Hi Eyck,

Thanks for your prompt repsonse!

I'm a bit confused about your proposal since my goal is to remove polling thread but still makes SC kernel alive.

I pasted my code below. You can see I made "polling thread" as an external thread, it notifies the "proccessing thread" by ThreadSafeEvent. However, to keep SC kernel not exits, I must put an extra SC_THREAD "main", otherwise there is only one single "wait" (in processing thread) event in sc kernel and it will ends.

The extra SC_THREAD "main" gets unexpected time advancing involved which is not what I expect.

#include <systemc>
#include <pthread.h>
#include <unistd.h>

using namespace sc_core;

class ThreadSafeEventIf : public sc_interface {
    virtual void notify(sc_time delay = SC_ZERO_TIME) = 0;
    virtual const sc_event &default_event(void) const = 0;
protected:
    virtual void update(void) = 0;
};

class ThreadSafeEvent : public sc_prim_channel, public ThreadSafeEventIf {
public:
    ThreadSafeEvent(const char *name = ""): event(name) {}

    void notify(sc_time delay = SC_ZERO_TIME) {
        this->delay = delay;
        async_request_update();
    }

    const sc_event &default_event(void) const {
        return event;
    }
protected:
    virtual void update(void) {
        event.notify(delay);
    }
    sc_event event;
    sc_time delay;
};

sc_event GenScEvent;

SC_MODULE(Foo) {
public:
    SC_CTOR(Foo) {
        SC_THREAD(main);

        SC_METHOD(eventTriggered);
        sensitive << threadSafeEvent;
        dont_initialize();
    }
private:
    void main() {   //extra forever thread to avoid simulation exit
        while (1) {
            wait(100, SC_NS);
        }
    }

    void eventTriggered() {
        printf("Foo: Got event from pthread \n");
        GenScEvent.notify();
    }
public:
    ThreadSafeEvent threadSafeEvent;
};

void *PollingThread(void *arg) {
    int cnt = 0;
    while (1) {
        cnt++;
        printf("[POLL]: %d: Before generating event from PollingThread \n", cnt);
        usleep(3*1000*1000);
        Foo *foo = (Foo*)(arg);
        foo->threadSafeEvent.notify();
        printf("[POLL]: %d: Event notified from PollingThread \n", cnt);
    }
};

class sc_top : public sc_module {
private:
    SC_HAS_PROCESS(sc_top);
public:
    sc_top(sc_module_name name="SCTOP"): sc_module(name) {
        SC_THREAD(processing_thread);
    }
    void processing_thread();
};

void sc_top::processing_thread() {
    int cnt =0;
    while (1) {
        printf("[PROC]: processing_thread called \n");
        printf("[PROC]: Wait GenScEvent \n");
        wait(GenScEvent);
        cnt++;
        printf("[PROC]: Got %d GenScEvent \n", cnt);
    }
}

int sc_main(int argc, char *argv[]) {
    Foo foo("foo");

    sc_top u_sc_top("u_sc_top");

    pthread_t thread;
    pthread_create(&thread, NULL, PollingThread, &foo);

    sc_start();

    printf("after sc_start \n");

    return 0;
}

 

BTW, I found below slides from GreenSoc which mentioned "async wait", but there is no details or examples.

https://workspace.accellera.org/document/dl/10932

Link to comment
Share on other sites

Well, your solution should work as well. But as far as I can tell your simulation will reach quite soon large timestamps as there is nothing preventing the SC kernel from continueing since your main thread in Foo just does a wait(). My proposal was to let the simulaton wait until some external event is being injected and then run for a certain period of time. This way you can control the time advance in the simulator.

Link to comment
Share on other sites

Hi Eyck,

My goal is to NOT involve any simulation time advancing in the SC modules. So I'm expecting a solution like:

Even with remove the "main" function in module(Foo), I can still make the standalone "wait(GenSvEvent)" keeps waiting for an external event (which is like SC simulation kernel is halt), instead of simulation quit. This requirement is mentioned in the Greensoc slides I posted, but I have no idea how to implement it.

 

Link to comment
Share on other sites

Hmm, I guess you lost me a bit.

If you want to stop the kernel to process further event and advance time you just need to not return control to the kernel e.g. waiting for a socket event. After receiving soemthing you need to return control to the SC kernel by calling wait(). If the processing needs some SystemC time to happen you need to allow the kernel to advance.

Waiting for an event is not a halt for the simulation kernel, it is just a halt for the thread calling the wait since you return control to the kernel.

Link to comment
Share on other sites

  • 7 months later...

hi yaohe, I understand your question and I met same problem too.   We want the simulation keep alive and wait asyn events, but the extra SC_THREAD "main"(as you said) destroy simulation time.  Do you solve this problem now?  Or I hope the example blew can solve  it:

#include <systemc.h>
#include <pthread.h>
#include <unistd.h>

using namespace std;
class ThreadSafeEventIf : public sc_interface {
        virtual void notify(sc_time delay = SC_ZERO_TIME) = 0;
        virtual const sc_event &default_event(void) const = 0;
    protected:
        virtual void update(void) = 0;
};

class ThreadSafeEvent : public sc_prim_channel, public ThreadSafeEventIf {
    public:
        ThreadSafeEvent(const char *name = ""): event(name) {}

        void notify(sc_time delay = SC_ZERO_TIME) {
            this->delay = delay;
            async_request_update();
        }

        const sc_event &default_event(void) const {
            return event;
        }
    protected:
        virtual void update(void) {
            event.notify(delay);
        }
        sc_event event;
        sc_time delay;
};

sc_event GenScEvent;
sc_event workingFinishEvent;  // finish event
int workingFlag = 0;  // maybe dnot need a lock

SC_MODULE(Foo) {
    public:
        SC_CTOR(Foo) {
            SC_THREAD(main);

            SC_METHOD(eventTriggered);
            sensitive << threadSafeEvent;
            dont_initialize();
        }
    private:
        void main() {   //extra forever thread to avoid simulation exit
            while (1) {
                usleep(1*1000*1000);  // check if there is any instruction every one sec.
                wait(SC_ZERO_TIME);
                if(workingFlag){    // check working
                    wait(workingFinishEvent);  // wait the working finish 
                }
                usleep(1*1000*1000);
            }
        }

        void eventTriggered() {
            // printf("Foo: Got event from pthread \n");
            GenScEvent.notify();
        }
    public:
        ThreadSafeEvent threadSafeEvent;
};

void* PollingThread(void* arg) {
    int cnt = 0;
    while (1) {
        cnt++;
        printf("[POLL]: %d: Before generating event from PollingThread \n", cnt);
        usleep(3*1000*1000);
        Foo *foo = (Foo*)(arg);
        foo->threadSafeEvent.notify();
        printf("[POLL]: %d: Event notified from PollingThread \n", cnt);

    }
};

class sc_top : public sc_module {
    private:
        SC_HAS_PROCESS(sc_top);
    public:
        sc_top(sc_module_name name="SCTOP"): sc_module(name) {
            SC_THREAD(processing_thread);
        }
        void processing_thread();
};

void sc_top::processing_thread() {
    int cnt =0;
    while (1) {
        printf("[PROC]: processing_thread called \n");
        cout << "[PROC]: Wait GenScEvent  time: " << sc_time_stamp();
        wait(GenScEvent);
        workingFlag = 1;
        cnt++;
        wait(10, SC_SEC);  // advance simulation time
        cout << "[PROC]: Got and Finish "<<cnt << " GenScEvent   time: " << sc_time_stamp();
        workingFinishEvent.notify();
        workingFlag = 0;
    }
}

int sc_main(int argc, char *argv[]) {
    Foo foo("foo");
    sc_top u_sc_top("u_sc_top");

    pthread_t thread;
    pthread_create(&thread, NULL, PollingThread, &foo); 

    sc_start();

    return 0;
}

 

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...