payload_event_queue.h from GreenSocs at Krugle
Show payload_event_queue.h syntax highlighted
/*****************************************************************************
The following code is derived, directly or indirectly, from the SystemC
source code Copyright (c) 1996-2005 by all Contributors.
All Rights reserved.
The contents of this file are subject to the restrictions and limitations
set forth in the SystemC Open Source License Version 2.4 (the "License");
You may not use this file except in compliance with such restrictions and
limitations. You may obtain instructions on how to receive a copy of the
License at http://www.systemc.org/. Software distributed by Contributors
under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF
ANY KIND, either express or implied. See the License for the specific
language governing rights and limitations under the License.
*****************************************************************************/
/*****************************************************************************
Modified from
payload_event_queue.h -- Event Queue Facility Definitions
Original Author: Ulli Holtmann, Synopsys, Inc.
*****************************************************************************/
/*****************************************************************************
MODIFICATION LOG - modifiers, enter your name, affiliation, date and
changes you are making here.
Name, Affiliation, Date: Marcus Bartholomeu, GreenSocs, 2006-05
Description of Modification: starting from the original sc_event_queue.cpp,
a payload was associated to each event trigger.
*****************************************************************************/
#ifndef PAYLOAD_EVENT_QUEUE_H
#define PAYLOAD_EVENT_QUEUE_H
#include "sysc/communication/sc_interface.h"
#include "sysc/kernel/sc_module.h"
#include "sysc/kernel/sc_event.h"
#include <map>
using std::multimap;
using std::pair;
namespace tlm {
template <typename PAYLOAD>
class timeorderedlist
{
public:
struct element
{
struct element *next;
PAYLOAD p;
sc_time t;
element(PAYLOAD p, sc_time t): p(p),t(t) {}
};
element *empties;
element *list;
unsigned int size;
timeorderedlist(){
empties=NULL;
list=NULL;
size=0;
}
~timeorderedlist(){
while(size) {
delete_top();
}
// while(empties){
// struct element *e=empties->next;
// delete empties;
// empties=e;
// }
}
void insert(PAYLOAD p, sc_time t){
if (!empties) {
empties=new struct element(p,t);
empties->next=NULL;
}
struct element *e=empties;
empties=empties->next;
e->p=p;
e->t=t;
struct element * ancestor=NULL;
struct element * iterator=list;
while (iterator && iterator->t<=t){
ancestor=iterator;
iterator=iterator->next;
}
if (ancestor==NULL){
e->next=list;
list=e;
}
else {
e->next=iterator;
ancestor->next=e;
}
size++;
}
void delete_top(){
if (list) {
struct element *e=list;
list=list->next;
e->next=empties;
empties=e;
size--;
}
}
unsigned int getSize()
{
return size;
}
PAYLOAD top()
{
return list->p;
}
sc_time top_time()
{
return list->t;
}
sc_time next_time()
{
return list->next->t;
}
};
//private:
//timeorderedlist *empties;
//timeorderedlist *m_ppq;
//---------------------------------------------------------------------------
/**
* Interface to the event queue with payload
*/
//---------------------------------------------------------------------------
template<typename PAYLOAD>
class payload_event_queue_if : public virtual sc_interface
{
public:
virtual void notify (PAYLOAD p) =0;
virtual void notify (PAYLOAD p, double when, sc_time_unit base) =0;
virtual void notify (PAYLOAD p, const sc_time& when) =0;
virtual void cancel_all() =0;
virtual PAYLOAD get_payload() =0;
//virtual const bool fired() =0;
virtual void wait(PAYLOAD) =0;
};
#ifdef DUST_ENABLE
# define DUST_PEQ_STREAM_NAME "peq_stream"
# define DUST_PEQ_STREAM_KIND "transactor"
# define DUST_PEQ_GEN_NAME "peq_gen"
# define DUST_PEQ_CALL_BEGIN_NAME "begin"
# define DUST_PEQ_CALL_END_NAME "end"
# define DUST_PEQ_TRANSACTION_CONTAINER_NAME "transaction_container"
# define DUST_PEQ_PHASE_NAME "phase"
# define DUST_PEQ_DATA_PREFIX "data_"
# define DUST_RECORD_TRANSACTION_DATA_WIDTH 64
#endif
//---------------------------------------------------------------------------
/**
* An event queue that can contain any number of pending
* notifications. Each notification have an associate payload.
*/
//---------------------------------------------------------------------------
template<typename PAYLOAD>
class payload_event_queue:
public payload_event_queue_if<PAYLOAD>,
public sc_module
{
public:
SC_HAS_PROCESS( payload_event_queue );
payload_event_queue();
payload_event_queue( sc_module_name name_ );
~payload_event_queue();
virtual void notify (PAYLOAD);
virtual void notify (PAYLOAD, const sc_time& when);
virtual void notify (PAYLOAD, double when, sc_time_unit base);
virtual void cancel_all();
virtual const sc_event& default_event() const;
// virtual const bool fired();
virtual bool advance();
virtual void wait(PAYLOAD p);
PAYLOAD get_payload();
private:
void fire_event();
private:
timeorderedlist<PAYLOAD> m_ppq;
sc_event m_e; // default event
sc_dt::uint64 m_delta;
sc_time m_time;
private:
#ifdef DUST_ENABLE
/* for DUST SCV transaction recording */
scv_tr_stream peq_stream;
scv_tr_generator<std::string, std::string> peq_gen;
#endif
};
//---------------------------------------------------------------------------
//
// Implementation (in the header to avoid use of export templates)
//
//---------------------------------------------------------------------------
template<typename PAYLOAD>
void payload_event_queue<PAYLOAD>::notify (PAYLOAD p, const sc_time& when)
{
m_ppq.insert(p, when + sc_time_stamp() );
m_e.notify(when); // note, this will only over-right the "newest" event.
}
template<typename PAYLOAD>
void payload_event_queue<PAYLOAD>::notify (PAYLOAD p)
{
// notify( p, SC_ZERO_TIME ); // this is the original implementation, which adds one delta
// TODO: this is Mark's no-delta patch, which might results in a problem for UserAPIs, if
// master and slave ports are directly connected:
// After ACKRequest has been received, the second get_payload() call of the slave
// (after the master's SendData) delivers the wrong payload (i.e. the payload
// of the request phase, and not the payload of the data phase)! The reason is,
// that both Request and SendData are called during the same delta cycle by the master.
// To work around this "problem", a UserAPIs either can use notify(PAYLOAD, SC_ZERO_TIME)
// or has to call wait(SC_ZERO_TIME).
m_ppq.insert(p, sc_time_stamp() );
m_e.notify(); // note, this will only over-right the "newest" event.
}
template<typename PAYLOAD>
void payload_event_queue<PAYLOAD>::notify (PAYLOAD p, double when, sc_time_unit base )
{
notify( p, sc_time(when,base) );
}
template<typename PAYLOAD>
const sc_event& payload_event_queue<PAYLOAD>::default_event() const
{
return m_e;
}
template<typename PAYLOAD>
bool payload_event_queue<PAYLOAD>::advance()
{
m_ppq.delete_top(); // eat top;
if (m_ppq.getSize() > 0) {
sc_time now=sc_time_stamp();
sc_time top=m_ppq.top_time();
if (top > now){
m_e.notify( top - now);
return false;
} else {
assert(m_delta==sc_delta_count());
m_e.cancel();
return true;
}
} else {
return false;
}
}
template<typename PAYLOAD>
PAYLOAD payload_event_queue<PAYLOAD>::get_payload()
{
sc_time now=sc_time_stamp();
sc_time top=m_ppq.top_time();
uint64 delta=sc_delta_count();
if(m_time!=now) {//first "get_payload" call for this point of sim time
m_time=now;
m_delta=sc_delta_count();
}
while(top < now)
{ // while it's an old one
m_ppq.delete_top();
top=m_ppq.top_time();
m_delta=delta;
}
if (delta > m_delta ) { // while we keep asking in the same delta - keep giving the same result
m_ppq.delete_top();
top=m_ppq.top_time();
m_delta=delta;
}
return m_ppq.top();
}
// template<typename PAYLOAD>
// const bool payload_event_queue<PAYLOAD>::fired()
// {
// return m_delta == sc_delta_count();
// }
template<typename PAYLOAD>
void payload_event_queue<PAYLOAD>::wait(PAYLOAD p)
{
//RG: do we need this?
sc_core::wait(default_event());
while (p != get_payload()) {
sc_core::wait(default_event());
}
}
#ifdef DUST_RECORD_TRANSACTIONS
template<typename PAYLOAD>
payload_event_queue<PAYLOAD>::payload_event_queue()
: sc_module( sc_gen_unique_name( "payload_event_queue" ) ),
m_delta(0),m_time(0-10,SC_NS),
peq_stream(DUST_PEQ_STREAM_NAME, DUST_PEQ_STREAM_KIND),
peq_gen(DUST_PEQ_GEN_NAME, peq_stream,
DUST_PEQ_CALL_BEGIN_NAME, DUST_PEQ_CALL_END_NAME)
{
SC_METHOD( fire_event );
sensitive << m_e;
dont_initialize();
end_module();
}
template<typename PAYLOAD>
payload_event_queue<PAYLOAD>::payload_event_queue( sc_module_name name_ )
: sc_module( name_ ),
m_delta(0),m_time(0-10,SC_NS),
peq_stream(DUST_PEQ_STREAM_NAME, DUST_PEQ_STREAM_KIND),
peq_gen(DUST_PEQ_GEN_NAME, peq_stream,
DUST_PEQ_CALL_BEGIN_NAME, DUST_PEQ_CALL_END_NAME)
{
SC_METHOD( fire_event );
sensitive << m_e;
dont_initialize();
end_module();
}
#else
template<typename PAYLOAD>
payload_event_queue<PAYLOAD>::payload_event_queue()
: sc_module( sc_gen_unique_name( "payload_event_queue" ) ),
m_delta(0),m_time(0-10,SC_NS)
{
SC_METHOD( fire_event );
sensitive << m_e;
dont_initialize();
end_module();
}
template<typename PAYLOAD>
payload_event_queue<PAYLOAD>::payload_event_queue( sc_module_name name_ )
: sc_module( name_ ),
m_delta(0),m_time(0-10,SC_NS)
{
SC_METHOD( fire_event );
sensitive << m_e;
dont_initialize();
end_module();
}
#endif
template<typename PAYLOAD>
payload_event_queue<PAYLOAD>::~payload_event_queue()
{
}
template<typename PAYLOAD>
void payload_event_queue<PAYLOAD>::cancel_all()
{
while( m_ppq.getSize() > 0 )
m_ppq.delete_top();
m_e.cancel();
}
template<typename PAYLOAD>
void payload_event_queue<PAYLOAD>::fire_event()
{
sc_time now=sc_time_stamp();
sc_time top=m_ppq.top_time();
// uint64 delta=sc_delta_count();
while(top < now) //in case fire_event() triggers before any of the get_payload calls, we have to do the deletion of the old stuff here
{ // while it's an old one
m_ppq.delete_top();
top=m_ppq.top_time();
}
// DUST transaction recording
#ifdef DUST_RECORD_TRANSACTIONS
PAYLOAD p = m_ppq.top();
// todo: put some sense into the begin and end strings
scv_tr_handle h = peq_gen.begin_transaction(std::string(DUST_PEQ_CALL_BEGIN_NAME));
// record phase
h.record_attribute(DUST_PEQ_PHASE_NAME, p.second);
// record transaction container
h.record_attribute(DUST_PEQ_TRANSACTION_CONTAINER_NAME, *(p.first));
#ifdef DUST_ALSO_RECORD_DATA
// record transaction data if in data phase
if (p.second.isDataValid() || p.second.isResponseValid()) {
MData d;
d.set((*(p.first)).get_mData());
char text[200];
char data[DUST_RECORD_TRANSACTION_DATA_WIDTH];
memset(data, 0, DUST_RECORD_TRANSACTION_DATA_WIDTH);
for (unsigned int i=0; i<d.byte_size(); i+=DUST_RECORD_TRANSACTION_DATA_WIDTH) {
sprintf(text, "data_%d", i);
memcpy(data, d.get()+i, i+DUST_RECORD_TRANSACTION_DATA_WIDTH>d.byte_size() ? d.byte_size()-i : DUST_RECORD_TRANSACTION_DATA_WIDTH);
h.record_attribute(text, data);
}
}
#endif
peq_gen.end_transaction(h, std::string(DUST_PEQ_CALL_END_NAME));
#endif
// all thats left is to notify the next event
if ( m_ppq.getSize() > 1 ) {
m_e.notify( m_ppq.next_time() - now) ;
}
}
} // namespace tlm
#endif // PAYLOAD_EVENT_QUEUE_H
See more files for this project here