Clement Deschamps
Builds for 1 pipeline passed in 6 minutes 23 seconds

greenthreads: refactored and cleaned up

#include <greenserialsocket/tlm_serial_bidirectional_socket.h>
#include <greenthreads/thread_safe_event.h>
#include <greenthreads/greenthreads.h>
#include <ncurses.h>
#include <queue>
... ... @@ -7,7 +7,7 @@
class StdioSerial : sc_module
{
protected:
gs::gt::gs_event_async m_async_event;
gs::gt::async_event m_async_event;
std::queue<unsigned char> m_queue;
... ...
... ... @@ -23,7 +23,7 @@
#include <vector>
#include <greensocket/target/multi_socket.h>
#include <greensocket/initiator/multi_socket.h>
#include <greenthreads/thread_safe_event.h>
#include <greenthreads/greenthreads.h>
... ...
#ifndef GS_GT_ASYNC_EVENT_H_
#define GS_GT_ASYNC_EVENT_H_
#ifndef SC_INCLUDE_DYNAMIC_PROCESSES
#define SC_INCLUDE_DYNAMIC_PROCESSES
#endif
#include <cerrno>
#include <list>
#include <pthread.h>
#include <semaphore.h>
#include <systemc.h>
#include <time.h>
#include <tlm_utils/tlm_quantumkeeper.h>
#include "greenthreads/util.h"
//#define SC_HAS_ASYNC_ATTACH_SUSPENDING
namespace gs {
namespace gt {
class before_end_of_delta_helper_if {
public:
virtual void simulation_phase_callback() = 0;
};
class before_end_of_delta_helper : sc_core::sc_module {
before_end_of_delta_helper_if *parent;
sc_core::sc_event endofdeltaEvent;
void endofdeltaMethod() {
if (sc_pending_activity()) {
endofdeltaEvent.notify(sc_time_to_pending_activity() + sc_time(0, SC_PS));
} else {
if (parent) {
parent->simulation_phase_callback();
}
// ensure events get back into the kernel
endofdeltaEvent.notify(sc_time(0, SC_PS));
}
}
public:
SC_HAS_PROCESS(before_end_of_delta_helper);
before_end_of_delta_helper(sc_module_name n, before_end_of_delta_helper_if *p) {
parent = p;
SC_METHOD(endofdeltaMethod);
sensitive << endofdeltaEvent;
}
};
class async_event : public sc_core::sc_prim_channel, public sc_event, before_end_of_delta_helper_if {
private:
static sem_i global_semaphore;
#ifndef SC_HAS_ASYNC_ATTACH_SUSPENDING
static before_end_of_delta_helper *helper;
#endif
private:
sc_core::sc_time m_delay;
spin_mutex local_mutex;
int outstanding;
public:
async_event(const char *name = "") : local_mutex(), outstanding(0) {
#ifdef SC_HAS_ASYNC_ATTACH_SUSPENDING
async_attach_suspending();
#else
if (!helper) {
helper = new before_end_of_delta_helper("before_end_of_delta_helper", this);
}
#endif
}
void notify(sc_core::sc_time delay = SC_ZERO_TIME) {
#ifndef SC_HAS_ASYNC_ATTACH_SUSPENDING
local_mutex.lock();
#endif
m_delay = delay;
async_request_update();
#ifndef SC_HAS_ASYNC_ATTACH_SUSPENDING
global_semaphore.post(); // post token to ensure SystemC execution
outstanding++;
local_mutex.unlock();
#endif
}
protected:
void update(void) {
// we should be in SystemC thread
#ifndef SC_HAS_ASYNC_ATTACH_SUSPENDING
local_mutex.lock();
#endif
sc_event::notify(m_delay);
#ifndef SC_HAS_ASYNC_ATTACH_SUSPENDING
for (; outstanding > 0; outstanding--) {
global_semaphore.wait();
}
local_mutex.unlock();
#endif
}
public:
void simulation_phase_callback() {
// As we are in the systemc thread, if we are chasing another process
// which locks, that is 'unlocked' we will see the resulting pending activity,
// so we will not lock here.
if (!sc_pending_activity()) {
global_semaphore.wait(); // No pending activity, and the global semaphore
// has nothing in it - so wait.
global_semaphore.post(); // replace the token we used.
}
}
};
}
}
#endif
... ...
#include "greenthreads/sync.h"
#include "greenthreads/async_event.h"
#include "greenthreads/inlinesync.h"
... ...
... ... @@ -3,15 +3,12 @@
#include <systemc.h>
#include <tlm.h>
//#include <tlm_utils/simple_initiator_socket.h>
//#include <tlm_utils/simple_target_socket.h>
#include "greenthreads/thread_safe_event.h"
#include <pthread.h>
//#include <greensocket/generic/gs_extension.h>
#include "greenthreads/sync.h"
#include <greensocket/target/multi_socket.h>
#include <greensocket/initiator/multi_socket.h>
#include <greenrouter/genericRouter_if.h>
#include <greenrouter/genericRouter.h>
#include <greenrouter/protocol/SimpleBus/simpleBusProtocol.h>
... ... @@ -32,7 +29,7 @@ namespace gs {
private:
gs::gp::GenericRouter<BUSWIDTH> *internalRouter;
event_async processTxnEvent;
async_event processTxnEvent;
pthread_t mainThread;
unsigned int l_port;
... ... @@ -44,8 +41,8 @@ namespace gs {
gs::gt::sem_i txnDone;
gs::gt::spin_mutex txnMutex;
event_async processDmiEvent;
event_async processDbgEvent;
async_event processDmiEvent;
async_event processDbgEvent;
public:
... ...
#ifndef GS_GT_SYNC_H_
#define GS_GT_SYNC_H_
#ifndef SC_INCLUDE_DYNAMIC_PROCESSES
#define SC_INCLUDE_DYNAMIC_PROCESSES
#endif
#include <cerrno>
#include <list>
#include <pthread.h>
#include <semaphore.h>
#include <systemc.h>
#include <time.h>
#include <tlm_utils/tlm_quantumkeeper.h>
#include "greenthreads/util.h"
#include "greenthreads/async_event.h"
#define DECOUPLED
#define COUT if (0) cout << pthread_self() << ":"
namespace gs {
namespace gt {
class centralSyncPolicy : sc_core::sc_module {
public:
static centralSyncPolicy share;
private:
std::list<sc_core::sc_time> endTimes;
spin_mutex mutex;
timed_cond ahead;
sc_core::sc_time backWindow;
sc_core::sc_time frontWindow;
bool sc_is_back_window;
sem_i canLock;
int locksToDo;
async_event checkWindowEvent;
/* It is always safe to call this method from anywhere in the SystemC thread */
void takeLockMethod() {
mutex.lock();
for (; locksToDo > 0; locksToDo--) {
while (sem_trywait(&canLock()) != 0) {
}
}
mutex.unlock();
}
public:
void releaseLock() {
canLock.post();
}
void takeLock() {
mutex.lock();
locksToDo++;
checkWindowEvent.notify();
mutex.unlock();
}
void kickLock() {
mutex.lock();
canLock.post();
locksToDo++;
checkWindowEvent.notify();
mutex.unlock();
}
SC_CTOR(centralSyncPolicy)
: endTimes(), mutex()
, backWindow(SC_ZERO_TIME), frontWindow(SC_ZERO_TIME), sc_is_back_window(false)
, canLock(0), locksToDo(0)
{
/* Prevent sc_stop posting an invisible event (causing us to hang) */
sc_core::sc_set_stop_mode(SC_STOP_IMMEDIATE);
SC_METHOD(checkWindow);
sensitive << checkWindowEvent;
dont_initialize();
}
void start_of_simulation() {
/*
* Need to wait till start of simulation to start the window,
* because otherwise the quantum may not be set.
*/
checkWindowEvent.notify(tlm_utils::tlm_quantumkeeper::get_global_quantum());
COUT << " quantum " << tlm_utils::tlm_quantumkeeper::get_global_quantum() << "\n";
}
~centralSyncPolicy() {
/*
* Take the lock before you exit, will force others out of the critical region
* (preventing segfault on exit)
*/
mutex.lock();
}
void setWindow(sc_core::sc_time t, sc_core::sc_time *entryRef, bool decoupled)
{
*entryRef = t;
update_window();
/* wait if we're ahead the backWindow plus a quantum */
sc_core::sc_time quantum = tlm_utils::tlm_quantumkeeper::get_global_quantum();
while (!decoupled && (t > backWindow + quantum)) {
if (ahead.wait()) {
COUT << "Timeout reached\n";
break;
}
}
}
sc_core::sc_time getBackWindow() { return backWindow; }
sc_core::sc_time getFrontWindow() { return frontWindow; }
sc_core::sc_time *registerLockable() {
mutex.lock();
endTimes.push_back(SC_ZERO_TIME);
sc_core::sc_time *entryRef = &(endTimes.back());
mutex.unlock();
return entryRef;
}
/*
* we are called when the window changes, or we call ourself
* Or - critically - we get interupted by a txn, and then the txn
* finishes, we must be called again....
* If we are behind, notify ourself in the future, and check again.
* If we are ahead, sleep till we're woken again.
*/
void checkWindow()
{
if (sc_is_back_window) {
update_window();
}
if (sc_time_stamp() < frontWindow) {
checkWindowEvent.notify(frontWindow - sc_time_stamp());
} else {
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 1;
/* Ensure there are no pending token requests. */
takeLockMethod();
if (sem_timedwait(&(canLock()), &ts) == 0) {
/* critical we take the lock here, incase the 'kickLock' is about to post a notify */
mutex.lock();
sem_post(&(canLock()));
mutex.unlock();
/* we rely on checkWindowEvent being notified at the end of the transaction */
} else {
checkWindowEvent.notify();
}
}
}
protected:
void update_window()
{
sc_core::sc_time sc_ts = sc_time_stamp();
sc_core::sc_time back = sc_ts;
sc_core::sc_time front = sc_ts;
bool changed = false;
mutex.lock();
for (std::list<sc_core::sc_time>::iterator i = endTimes.begin(); i != endTimes.end(); ++i) {
if (*i < back) {
back = *i;
}
else if (*i > front) {
front = *i;
}
}
if (back != backWindow) {
changed = true;
backWindow = back;
}
if (frontWindow != front) {
changed = true;
frontWindow = front;
}
sc_is_back_window = (sc_ts == backWindow);
mutex.unlock();
if (changed) {
ahead.release();
kickLock();
}
}
};
class syncSource {
sc_core::sc_time *handle;
sc_core::sc_time back_window;
pthread_t owner_thread;
bool decoupled;
public:
syncSource(bool _decoupled = false) : decoupled(_decoupled) {
owner_thread = pthread_self();
handle = centralSyncPolicy::share.registerLockable();
back_window = SC_ZERO_TIME;
centralSyncPolicy::share.setWindow(back_window, handle, decoupled);
}
void syncAt(sc_core::sc_time t) {
if (pthread_equal(pthread_self(), owner_thread)) {
return;
}
if (t > back_window) {
back_window = t;
if (decoupled) {
back_window = sc_time_stamp();
}
/* setWindow could lock if WE have got ahead (SystemC is behind) */
centralSyncPolicy::share.setWindow(back_window, handle, decoupled);
}
}
};
class decoupledSource : public syncSource {
public:
decoupledSource() : syncSource(true) {}
};
}
}
#undef COUT
#endif
... ...
#ifndef GS_GT_UTIL_H_
#define GS_GT_UTIL_H_
// TODO: includes
namespace gs {
namespace gt {
/** Convenience semaphore implementation */
class sem_i {
sem_t sem;
public:
sem_i(int i) { sem_init(&sem, 0, i); }
sem_t &operator()() { return sem; }
void wait() {
while (sem_wait(&sem) != 0) {
};
}
void post() { sem_post(&sem); }
};
/** Convenience mutex implementation */
class spin_mutex {
pthread_spinlock_t mtx;
public:
spin_mutex() { pthread_spin_init(&mtx, 0); }
void lock() { pthread_spin_lock(&mtx); }
void unlock() { pthread_spin_unlock(&mtx); }
};
/** Convenience timed cond implementation */
class timed_cond {
pthread_cond_t cnd;
pthread_mutex_t mutex;
int state;
public:
timed_cond() {
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cnd, NULL);
}
bool wait() {
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 1; // Wait 1 second
pthread_mutex_lock(&mutex);
int p = pthread_cond_timedwait(&cnd, &mutex, &ts);
pthread_mutex_unlock(&mutex);
return p != 0;
}
void release() {
pthread_mutex_lock(&mutex);
pthread_cond_broadcast(&cnd);
pthread_mutex_unlock(&mutex);
}
};
}
}
#endif
... ...
#include "greenthreads/inlinesync.h"
gs::gt::sem_i gs::gt::gs_event_async::global_semaphore(0);
gs::gt::sem_i gs::gt::async_event::global_semaphore(0);
#ifndef SC_HAS_ASYNC_ATTACH_SUSPENDING
gs::gt::before_end_of_delta_helper *gs::gt::gs_event_async::helper = NULL;
gs::gt::before_end_of_delta_helper *gs::gt::async_event::helper = NULL;
#endif
gs::gt::centralSyncPolicy gs::gt::centralSyncPolicy::share("CentralSyncPolicy");
... ...