Jump to content

async_request_update example


Recommended Posts

I have a process that is asynchronous/external to SystemC Kernel. I want this process to generate an event to a systemC thread.

I read on forums that there is mechanism - "async_request_update" - in system 2.3.0 to do so without corrupting systemC kernel data structures.

Is there an example showing how to use this mechanism with OSCI simulator?

Thanks

Piyush

Link to post
Share on other sites

struct thread_safe_event_if

: sc_core::sc_interface

{

virtual void notify(sc_core::sc_time delay = SC_ZERO_TIME) = 0;

virtual const sc_core::sc_event& default_event(void) const = 0;

protected:

virtual void update(void) = 0;

};

struct thread_safe_event_channel

: sc_core::sc_prim_channel, thread_safe_event_if

{

thread_safe_channel(const char* name);

void notify(sc_core::sc_time delay = SC_ZERO_TIME);

const sc_core::sc_event& default_event(void) const;

protected:

virtual void update(void);

private:

sc_core::sc_event m_event;

sc_core::sc_time m_delay;

};

// The following may be safely called from outside the SystemC OS thread

void thread_safe_event_channel::notify(sc_core::sc_time delay)

{

m_delay = delay;

async_request_update();

}

const sc_core::sc_event& thread_safe_event_channel::default_event(void)

{

return m_event;

}

virtual void update(void)

{

m_event.notify(m_delay);

}

Link to post
Share on other sites

There is no real tutorial example available, yet. But you can look at the SystemC regression tests [1], which have a small test included at

tests/systemc/1666-2011-compliance/async_request_update/

Greetings from Oldenburg,

Philipp

[1] http://www.accellera..._2-3_2012-07-02

Hi Philipp

There is no test directory at the path specified by you in systemc_regressions-2.3.0. There is async_reset and async_reset_port but no async_request_update?

Regards

Piyush

Link to post
Share on other sites

Oh, sorry. My mistake. The mentioned test has not been part of the 2.3.0 release. :(

There may be some similar test included in the next version of SystemC…

For now, you should use David's example as a reference. As in any (host) parallel programming, make sure to properly protect any shared data used in both parts (SystemC and other OS threads). This is not automatically addressed by the async_request_update mechanism.

Sorry for the noise,

Philipp

Link to post
Share on other sites

I should probably note that using a non-default delay in my example probably does not make sense. Also this example could lose notifications in the sense that if SystemC has not yet serviced a notification, a subsequent notification will simply overwrite the previous one. I generally setup a handshake to ensure the external notifier knows it was received. You could also make a more elaborate notify method that sets/clears a "busy" flag. Be careful to properly protect shared data as Philipp points out.

Link to post
Share on other sites

Hi David and Philipp,

Thanks for the example. This example works for me.

As David mention that newer notification will simply overwrite previous notification even though time delta for newer is larger than previous notification. This is fine in my case as in my case the time delta is always zero. As long as event can be delivered i am fine.

To understand what is meant by shared data, let me elaborate my scenario.

The shared data structure in my case is a queue<int>.

My external thread looks as following

grab pthread_mutex

push a value in queue.

thread_safe_event.notify(); // from David's example

release pthread_mutex

SystemC thread

while(1)

{

wait( thread_safe_event.default_event() );

grab pthread_mutex

pop all values from queue.

release phtread_mutex;

process values poped from queue.

}

Does this cover shared data structure protection for my case or do i need add mutexing within thread_safe_event as well? I am asking the question because in David's example m_delay variable essentially a shared variable between external thread and systemC kernel. external thread writes to variable and systemC kernel reads the variable?

Thanks

Piyush

Link to post
Share on other sites

Your solution looks correct. You may want to consider wrapping all of this together in a single "thread-safe input queue" for better encapsulation (and to remove the pthread stuff from your SystemC model). In this case, you can duplicate the queues internally: At protected one for pushing from the outside and a SystemC-local one, where to store the tokens that you need to process within the SystemC part of the model. With a swap of these queues in the update step, you can keep the locking times minimal.

Secondly, if you accept a slight extension beyond the plain 1666-2011 standard, you can use sc_scoped_lock and sc_host_mutex instead of the plain pthread_mutex_* API (untested):

template< typename T>
class async_fifo
 : public tlm::tlm_blocking_get_if<T> // whatever fits your needs best
 , public sc_core::sc_prim_channel
{
 sc_core::sc_host_mutex mtx_; // host-locking (e.g. pthread_mutex)
 sc_core::sc_event  written_;
 std::deque<T> pushq_, popq_;

public:
 async_fifo( const char* nm )
: sc_core::sc_prim_channel(nm)
 {}

 /* implement SystemC side interface (e.g. tlm_blocking_get_if) based on pop-queue */

 void write_async( T const & v )
 {
{
  sc_core::sc_scoped_lock lock( mtx_ ); // critical section
  pusq_.push_back( v );	          // append new value to queue
}
async_request_update();
 }

private:
 void update() {
sc_assert( ! popq_.size() ); // make sure, everything is consumed by now
{
  sc_core::sc_scoped_lock lock( mtx_ ); // critical section
  popq_.swap( pushq_ );      	    // move all pending tokens to pop-queue
}
written_.notify( sc_core::SC_ZERO_TIME );
 }
}; // async_fifo

In David's original example, m_delay is indeed not fully protected. But as he already pointed out, you may miss updates (and notifications) anyhow. On the lower level, the compiler should be able to update m_delay in a single instruction, which at least prevents broken values. (You may want to check the assembly for this.)

Greetings from Oldenburg,

Philipp

Link to post
Share on other sites
  • 7 years later...

I have found a complete code example  (the location is shown at the bottom), that operates with wait(time); the method proposed by David Black, too. The source code argumentation seems to be reasonable, and the example works with the vast sleep times. It seems to me that for some reason the waiting does not work: the timing of the event makes the synchronization, rather than the event notification sent after the timing is over. The code below produces the output

SC started
External host thread started
Done 1
Done 2
Done 3
Event 1 notified from an external host thread
Event 2 notified from an external host thread
Got event
SC returned

With these timings, the second event is not sent at all, and neither of the wait() operations works. It looks that I missed the point, why.

#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_MODULE(Foo)
{
public:
    SC_CTOR(Foo)
    {
        SC_THREAD(main);

        SC_METHOD(eventTriggered);
        sensitive << event;
        dont_initialize();
     std::cout << "SC started\n";
    }
private:
    void main() {
        usleep(1000); // Just for the example, event is added to pending events during this sleep
        wait(SC_ZERO_TIME); // Schedule (event is evaluated here)
        std::cout << "Done 1" << std::endl;
         usleep(1000); // Just for the example
//        wait(SC_ZERO_TIME); // Schedule (event is evaluated here)
          wait(1,SC_MS);
        std::cout << "Done 2" << std::endl;
        usleep(1 * 1000 * 1000); // Just for the example, event is added to pending events during this sleep
        std::cout << "Done 3" << std::endl;
       usleep(2 * 1000 * 1000); // Just for the example, event is added to pending events during this sleep
   }

    void eventTriggered() {
        std::cout << "Got event" << std::endl;
    }

public:
    ThreadSafeEvent event;
};

void *externalHostThread(void *arg) {
    std::cout << "External host thread started" << std::endl;
    Foo* foo = (Foo*)(arg);
    usleep(1500 * 1000); // Just for the example
    std::cout << "Event 1 notified from an external host thread" << std::endl;
    foo->event.notify();
    usleep(1 * 1000 * 1000); // Just for the example
    std::cout << "Event 2 notified from an external host thread" << std::endl;
    foo->event.notify();
}

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

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

   sc_start();
    std::cout << "SC returned" << std::endl;
    return 0;
}

//https://stackoverflow.com/questions/49814756/async-request-update-example-in-systemc

 

Link to post
Share on other sites

It looks like I could not understand some essential points of

async_request_update()


In the sample program below (if the usleep in externalHostThread is commented out, the output is)
 

SC first event notified
ThreadSafeNotify called
External host thread started
Event 1 notified from an external host thread
ThreadSafeNotify called
Event 2 notified from an external host thread
ThreadSafeNotify called
ThreadSafeUpdate called
Got event in triggered
SC returned

if it is uncommented, the output is

SC started
SC first event notified
ThreadSafeNotify called
External host thread started
Event 1 notified from an external host thread
ThreadSafeNotify called
ThreadSafeUpdate called
Got event in triggered
SC returned

1/ That is, I do receive the event in the SC_METHOD, but not in the SC_THREAD

2./ If the delay (imitating some useful processing) is active, the 2nd event is not sent out, the thread returns early.

Whay do I wrong?

 

 

 

Link to post
Share on other sites

Nothing in SC_THREAD main has any dependence on anything other than time in your example. The external event will have not effect. If you want to see an event, you must be waiting on it. usleep only consumes time. wait(SC_ZERO_TIME) means that the simulation for that portion will wait zero simulated seconds and resume. You should add wait(event) to see it.

Link to post
Share on other sites

Sorry, I noticed that I did not insert the new source code

So, the output again is

SC started
SC first event notified
ThreadSafeNotify called
External host thread started
Event 1 notified from an external host thread
ThreadSafeNotify called
ThreadSafeUpdate called
Got event in triggered
SC returned

I am missing

1/ why the "Event2 notified" line is missing (the thread should terminate only after printing it)

As the output is prompt, I guess the thread terminates before reaching printing

The update() is called after the second notify(); I expected it is called after the third notify()

2/ I expected a "Got event in main()" message, too

 

#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;
    std::cout << "ThreadSafeNotify called\n";
        async_request_update();
    }

    const sc_event &default_event(void) const {
        return event;
    }
protected:
    virtual void update(void) {
        event.notify(delay);
     std::cout << "ThreadSafeUpdate called\n";
     }
    sc_event event;
    sc_time delay;
};

SC_MODULE(Foo)
{
public:
    SC_CTOR(Foo)
    {
        SC_THREAD(main);
        sensitive << event;

        SC_METHOD(eventTriggered);
        sensitive << event;
        dont_initialize();
     std::cout << "SC started\n";
     std::cout << "SC first event notified\n";
      event.notify();
    }
private:
    void main() {
    int i=0;
    while(i++<3)
    {
        wait();
        std::cout << "Got event in main()" << std::endl;
        }
}

    void eventTriggered() {
        std::cout << "Got event in triggered" << std::endl;
}

public:
    ThreadSafeEvent event;
};

void *externalHostThread(void *arg) {
    std::cout << "External host thread started" << std::endl;
    Foo* foo = (Foo*)(arg);
    std::cout << "Event 1 notified from an external host thread" << std::endl;
    foo->event.notify();
    usleep(1 * 1000 * 1000); // Just for the example
    std::cout << "Event 2 notified from an external host thread" << std::endl;
    foo->event.notify();
}

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

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

    sc_start();
    std::cout << "SC returned" << std::endl;
    return 0;
}

 

Link to post
Share on other sites

I am getting confused. My output is

SC started
External host thread started
Before Event 1 notified from an external host thread
ThreadSafeNotify called
After Event 1 notified from an external host thread
ThreadSafeUpdate called
Got event in triggered
Got event after next_trigger
Got event in triggered again
Initializing main()
SC returned

I expected

1/ main() is executed before anything starts

2/ also the "After usleep" and "Event 2" messages appear
 

#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;
    std::cout << "ThreadSafeNotify called\n";
        async_request_update();
    }

    const sc_event &default_event(void) const {
        return event;
    }
protected:
    virtual void update(void) {
        event.notify(delay);
     std::cout << "ThreadSafeUpdate called\n";
     }
    sc_event event;
    sc_time delay;
};

SC_MODULE(Foo)
{
public:
    SC_CTOR(Foo)
    {
        SC_THREAD(main);
        sensitive << event;

        SC_METHOD(eventTriggered);
        sensitive << event;
        dont_initialize();
     std::cout << "SC started\n";
    }
private:
    void main() {
       std::cout << "Initializing main()" << std::endl;
    int i=0;
    while(i++<3)
    {
        wait();
        std::cout << "Got event in main()" << std::endl;
        }
}

    void eventTriggered() {
        std::cout << "Got event in triggered" << std::endl;
        next_trigger(10,SC_NS);
        std::cout << "Got event after next_trigger" << std::endl;
        next_trigger();
        std::cout << "Got event in triggered again" << std::endl;
}

public:
    ThreadSafeEvent event;
};

void *externalHostThread(void *arg) {
    std::cout << "External host thread started" << std::endl;
    Foo* foo = (Foo*)(arg);
    std::cout << "Before Event 1 notified from an external host thread" << std::endl;
    foo->event.notify();
    std::cout << "After Event 1 notified from an external host thread" << std::endl;
    usleep(10 * 1000 * 1000); // Just for the example
    std::cout << "After usleep from an external host thread" << std::endl;
    std::cout << "Event 2 notified from an external host thread" << std::endl;
    foo->event.notify();
}

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

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

    sc_start();
    std::cout << "SC returned" << std::endl;
    return 0;
}

 

Link to post
Share on other sites

I wonder what is wrong with using usleep(). If I replace it with a dummy cycle, my output is as shown below

Interestingly/importantly:

1/ Initializing main() appears where I expected, and correspondingly the event is received also in main().

However, this seems to be a random event; most of the outputs show that main() is intialized only !after! the events serviced, see the 2nd output.

2/ Event2 is sent and received as expected, but 'main()' persist hiding

Maybe

async_request_update()

was not designed to be used right at the beginning of the simulation and something is not initalized?

 

SC started
External host thread initial processing
Before Event 1 notified from an external host thread
ThreadSafeNotify called
Initializing main()After Event 1 notified from an external host thread

Event 2 notified from an external host thread
ThreadSafeNotify called
ThreadSafeUpdate called
Got event in triggered
Got event after next_trigger(10,SC_NS)
Got event in triggered again
ThreadSafeUpdate called
Got event in triggered
Got event after next_trigger(10,SC_NS)
Got event in triggered again
Got event in main()
SC returned
SC started
External host thread initial processing
Before Event 1 notified from an external host thread
ThreadSafeNotify called
After Event 1 notified from an external host thread
Event 2 notified from an external host thread
ThreadSafeNotify called
ThreadSafeUpdate called
Got event in triggered
Got event after next_trigger(10,SC_NS)
Got event in triggered again
Initializing main()
SC returned

 

 

Link to post
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...