SCViewer/scv_tr_sqlite/scv4tlm/tlm2_recorder.h

723 lines
29 KiB
C++

/*
* tlm_recording.h
*
* Created on: 07.11.2015
* Author: eyck
*/
#ifndef TLM2_RECORDER_H_
#define TLM2_RECORDER_H_
#include <scv.h>
#include <tlm>
#include "tlm_gp_data_ext.h"
#include "tlm_recording_extension.h"
#include <map>
#include <string>
#include <vector>
namespace scv4tlm {
/*! \brief The TLM2 transaction extensions recorder interface
*
* This interface is used by the TLM2 transaction recorder. It can be used to register custom recorder functionality
* to also record the payload extensions
*/
template< typename TYPES = tlm::tlm_base_protocol_types>
struct tlm2_extensions_recording_if {
/*! \brief recording attributes in extensions at the beginning, it is intended to be overload as it does nothing
*
*/
virtual void recordBeginTx(scv_tr_handle& handle, typename TYPES::tlm_payload_type& payload) = 0;
/*! \brief recording attributes in extensions at the end, it is intended to be overload as it does nothing
*
*/
virtual void recordEndTx(scv_tr_handle& handle, typename TYPES::tlm_payload_type& payload) = 0;
virtual ~tlm2_extensions_recording_if(){}
};
/*! \brief The TLM2 transaction recorder
*
* This module records all TLM transaction to a SCV transaction stream for further viewing and analysis.
* The handle of the created transaction is storee in an tlm_extension so that another instance of the scv_tlm2_recorder
* e.g. further down the opath can link to it.
*/
template< typename TYPES = tlm::tlm_base_protocol_types>
class tlm2_recorder:
public virtual tlm::tlm_fw_transport_if<TYPES>,
public virtual tlm::tlm_bw_transport_if<TYPES>,
public sc_core::sc_object {
public:
SC_HAS_PROCESS(tlm2_recorder<TYPES>);
//! \brief the attribute to selectively enable/disable recording
sc_core::sc_attribute<bool> enable;
//! \brief the attribute to selectively enable/disable timed recording
sc_core::sc_attribute<bool> enableTimed;
//! \brief the port where fw accesses are forwarded to
sc_core::sc_port<tlm::tlm_fw_transport_if<TYPES> > fw_port;
//! \brief the port where bw accesses are forwarded to
sc_core::sc_port<tlm::tlm_bw_transport_if<TYPES> > bw_port;
/*! \brief The constructor of the component
*
* \param name is the SystemC module name of the recorder
* \param tr_db is a pointer to a transaction recording database. If none is provided the default one is retrieved.
* If this database is not initialized (e.g. by not calling scv_tr_db::set_default_db() ) recording is disabled.
*/
tlm2_recorder(bool recording_enabled = true, scv_tr_db* tr_db = scv_tr_db::get_default_db()){
this->tlm2_recorder::tlm2_recorder(sc_core::sc_gen_unique_name("tlm2_recorder"), recording_enabled, tr_db);
}
/*! \brief The constructor of the component
*
* \param name is the SystemC module name of the recorder
* \param tr_db is a pointer to a transaction recording database. If none is provided the default one is retrieved.
* If this database is not initialized (e.g. by not calling scv_tr_db::set_default_db() ) recording is disabled.
*/
tlm2_recorder(const char* name, bool recording_enabled = true, scv_tr_db* tr_db = scv_tr_db::get_default_db())
: sc_core::sc_object(name)
, enable("enable", recording_enabled)
, enableTimed("enableTimed", recording_enabled)
, fw_port(sc_core::sc_gen_unique_name("fw"))
, bw_port(sc_core::sc_gen_unique_name("bw"))
, mm(new RecodingMemoryManager())
, b_timed_peq(this, &tlm2_recorder::btx_cb)
, nb_timed_peq(this, &tlm2_recorder::nbtx_cb)
, m_db(tr_db)
, b_streamHandle(NULL)
, b_streamHandleTimed(NULL)
, b_trTimedHandle(3)
, nb_streamHandle(2)
, nb_streamHandleTimed(2)
, nb_fw_trHandle(3)
, nb_txReqHandle(3)
, nb_bw_trHandle(3)
, nb_txRespHandle(3)
, dmi_streamHandle(NULL)
, dmi_trGetHandle(NULL)
, dmi_trInvalidateHandle(NULL)
, extensionRecording(NULL)
{
}
virtual ~tlm2_recorder(){
delete b_streamHandle;
delete b_streamHandleTimed;
for(size_t i = 0; i<b_trTimedHandle.size(); ++i) delete b_trTimedHandle[i];
for(size_t i = 0; i<nb_streamHandle.size(); ++i) delete nb_streamHandle[i];
for(size_t i = 0; i<nb_streamHandleTimed.size(); ++i) delete nb_streamHandleTimed[i];
for(size_t i = 0; i<nb_fw_trHandle.size(); ++i) delete nb_fw_trHandle[i];
for(size_t i = 0; i<nb_txReqHandle.size(); ++i) delete nb_txReqHandle[i];
for(size_t i = 0; i<nb_bw_trHandle.size(); ++i) delete nb_bw_trHandle[i];
for(size_t i = 0; i<nb_txRespHandle.size(); ++i) delete nb_txRespHandle[i];
delete dmi_streamHandle;
delete dmi_trGetHandle;
delete dmi_trInvalidateHandle;
delete extensionRecording;
}
// TLM-2.0 interface methods for initiator and target sockets, surrounded with Tx Recording
/*! \brief The non-blocking forward transport function
*
* This type of transaction is forwarded and recorded to a transaction stream named "nb_fw" with current timestamps.
* \param trans is the generic payload of the transaction
* \param phase is the current phase of the transaction
* \param delay is the annotated delay
* \return the sync state of the transaction
*/
virtual tlm::tlm_sync_enum nb_transport_fw(typename TYPES::tlm_payload_type& trans, typename TYPES::tlm_phase_type& phase,
sc_core::sc_time& delay);
/*! \brief The non-blocking backward transport function
*
* This type of transaction is forwarded and recorded to a transaction stream named "nb_bw" with current timestamps.
* \param trans is the generic payload of the transaction
* \param phase is the current phase of the transaction
* \param delay is the annotated delay
* \return the sync state of the transaction
*/
virtual tlm::tlm_sync_enum nb_transport_bw(typename TYPES::tlm_payload_type& trans, typename TYPES::tlm_phase_type& phase,
sc_core::sc_time& delay);
/*! \brief The blocking transport function
*
* This type of transaction is forwarded and recorded to a transaction stream named "b_tx" with current timestamps. Additionally a "b_tx_timed"
* is been created recording the transactions at their annotated delay
* \param trans is the generic payload of the transaction
* \param delay is the annotated delay
*/
virtual void b_transport(typename TYPES::tlm_payload_type& trans, sc_core::sc_time& delay);
/*! \brief The direct memory interface forward function
*
* This type of transaction is just forwarded and not recorded.
* \param trans is the generic payload of the transaction
* \param dmi_data is the structure holding the dmi information
* \return if the dmi structure is valid
*/
virtual bool get_direct_mem_ptr(typename TYPES::tlm_payload_type& trans, tlm::tlm_dmi& dmi_data);
/*! \brief The direct memory interface backward function
*
* This type of transaction is just forwarded and not recorded.
* \param start_addr is the start address of the memory area being invalid
* \param end_addr is the end address of the memory area being invalid
*/
virtual void invalidate_direct_mem_ptr(sc_dt::uint64 start_addr, sc_dt::uint64 end_addr);
/*! \brief The debug transportfunction
*
* This type of transaction is just forwarded and not recorded.
* \param trans is the generic payload of the transaction
* \return the sync state of the transaction
*/
virtual unsigned int transport_dbg(typename TYPES::tlm_payload_type& trans);
/*! \brief get the current state of transaction recording
*
* \return if true transaction recording is enabled otherwise transaction recording is bypassed
*/
const bool isRecordingEnabled() const {
return m_db != NULL && enable.value;
}
void setExtensionRecording(tlm2_extensions_recording_if<TYPES>* extensionRecording){
this->extensionRecording=extensionRecording;
}
private:
//! \brief the struct to hold the information to be recorded on the timed streams
struct tlm_recording_payload: public TYPES::tlm_payload_type {
scv_tr_handle parent;
uint64 id;
tlm_recording_payload& operator=(const typename TYPES::tlm_payload_type& x){
id=(uint64)&x;
set_command(x.get_command());
set_address(x.get_address());
set_data_ptr(x.get_data_ptr());
set_data_length(x.get_data_length());
set_response_status(x.get_response_status());
set_byte_enable_ptr(x.get_byte_enable_ptr());
set_byte_enable_length(x.get_byte_enable_length());
set_streaming_width(x.get_streaming_width());
return (*this);
}
explicit tlm_recording_payload(tlm::tlm_mm_interface* mm) :
TYPES::tlm_payload_type(mm), parent(), id(0) {
}
};
//! \brief Memory manager for the tlm_recording_payload
struct RecodingMemoryManager: public tlm::tlm_mm_interface {
RecodingMemoryManager() :
free_list(0), empties(0) {
}
tlm_recording_payload* allocate() {
typename TYPES::tlm_payload_type* ptr;
if (free_list) {
ptr = free_list->trans;
empties = free_list;
free_list = free_list->next;
} else {
ptr = new tlm_recording_payload(this);
}
return (tlm_recording_payload*) ptr;
}
void free(typename TYPES::tlm_payload_type* trans) {
trans->reset();
if (!empties) {
empties = new access;
empties->next = free_list;
empties->prev = 0;
if (free_list)
free_list->prev = empties;
}
free_list = empties;
free_list->trans = trans;
empties = free_list->prev;
}
private:
struct access {
typename TYPES::tlm_payload_type* trans;
access* next;
access* prev;
};
access *free_list, *empties;
};
RecodingMemoryManager* mm;
//! peq type definition
struct recording_types {
typedef tlm_recording_payload tlm_payload_type;
typedef typename TYPES::tlm_phase_type tlm_phase_type;
};
//! event queue to hold time points of blocking transactions
tlm_utils::peq_with_cb_and_phase<tlm2_recorder, recording_types> b_timed_peq;
//! event queue to hold time points of non-blocking transactions
tlm_utils::peq_with_cb_and_phase<tlm2_recorder, recording_types> nb_timed_peq;
/*! \brief The thread processing the blocking accesses with their annotated times
* to generate the timed view of blocking tx
*/
void btx_cb(tlm_recording_payload& rec_parts, const typename TYPES::tlm_phase_type& phase);
/*! \brief The thread processing the non-blocking requests with their annotated times
* to generate the timed view of non-blocking tx
*/
void nbtx_cb(tlm_recording_payload& rec_parts, const typename TYPES::tlm_phase_type& phase);
//! transaction recording database
scv_tr_db* m_db;
//! blocking transaction recording stream handle
scv_tr_stream* b_streamHandle;
//! transaction generator handle for blocking transactions
scv_tr_generator<sc_dt::uint64, sc_dt::uint64>* b_trHandle[3];
//! timed blocking transaction recording stream handle
scv_tr_stream* b_streamHandleTimed;
//! transaction generator handle for blocking transactions with annotated delays
std::vector<scv_tr_generator<tlm::tlm_command, tlm::tlm_response_status>*> b_trTimedHandle;
std::map<uint64, scv_tr_handle> btx_handle_map;
enum DIR{FW, BW};
//! non-blocking transaction recording stream handle
std::vector<scv_tr_stream*> nb_streamHandle;
//! non-blocking transaction recording stream handle
std::vector<scv_tr_stream*> nb_streamHandleTimed;
//! transaction generator handle for forward non-blocking transactions
std::vector<scv_tr_generator<tlm::tlm_phase_enum, tlm::tlm_sync_enum>*> nb_fw_trHandle;
//! transaction generator handle for forward non-blocking transactions with annotated delays
std::vector<scv_tr_generator<>*> nb_txReqHandle;
map<uint64, scv_tr_handle> nbtx_req_handle_map;
//! transaction generator handle for backward non-blocking transactions
std::vector<scv_tr_generator<tlm::tlm_phase_enum, tlm::tlm_sync_enum>*> nb_bw_trHandle;
//! transaction generator handle for backward non-blocking transactions with annotated delays
std::vector<scv_tr_generator<>*> nb_txRespHandle;
map<uint64, scv_tr_handle> nbtx_last_req_handle_map;
//! dmi transaction recording stream handle
scv_tr_stream* dmi_streamHandle;
//! transaction generator handle for DMI transactions
scv_tr_generator<tlm_gp_data, tlm_dmi_data>* dmi_trGetHandle;
scv_tr_generator<sc_dt::uint64, sc_dt::uint64>* dmi_trInvalidateHandle;
tlm2_extensions_recording_if<TYPES>* extensionRecording;
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// implementations of functions
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template< typename TYPES>
void tlm2_recorder<TYPES>::b_transport(typename TYPES::tlm_payload_type& trans, sc_core::sc_time& delay) {
tlm_recording_payload* req;
if (!isRecordingEnabled()) {
fw_port->b_transport(trans, delay);
return;
}
if (b_streamHandle == NULL) {
string basename(this->name());
b_streamHandle = new scv_tr_stream((basename + ".blocking").c_str(), "TRANSACTOR", m_db);
b_trHandle[tlm::TLM_READ_COMMAND] = new scv_tr_generator<sc_dt::uint64, sc_dt::uint64>("read", *b_streamHandle, "start_delay",
"end_delay");
b_trHandle[tlm::TLM_WRITE_COMMAND] = new scv_tr_generator<sc_dt::uint64, sc_dt::uint64>("write", *b_streamHandle, "start_delay",
"end_delay");
b_trHandle[tlm::TLM_IGNORE_COMMAND] = new scv_tr_generator<sc_dt::uint64, sc_dt::uint64>("ignore", *b_streamHandle, "start_delay",
"end_delay");
}
// Get a handle for the new transaction
scv_tr_handle h = b_trHandle[trans.get_command()]->begin_transaction(delay.value(), sc_time_stamp());
tlm_gp_data tgd(trans);
/*************************************************************************
* do the timed notification
*************************************************************************/
if (enableTimed.value) {
req = mm->allocate();
req->acquire();
(*req) = trans;
req->parent = h;
req->id=h.get_id();
#ifdef DEBUG
cout<<"notify addition of parent with id "<<req->id<<" to btx_handle_map with delay "<<delay<<std::endl;
#endif
b_timed_peq.notify(*req, tlm::BEGIN_REQ, delay);
}
if(extensionRecording) extensionRecording->recordBeginTx(h, trans);
tlm_recording_extension* preExt = NULL;
trans.get_extension(preExt);
if (preExt == NULL) { // we are the first recording this transaction
preExt = new tlm_recording_extension(h, this);
trans.set_extension(preExt);
} else {
h.add_relation(rel_str(PREDECESSOR_SUCCESSOR), preExt->txHandle);
}
scv_tr_handle preTx(preExt->txHandle);
fw_port->b_transport(trans, delay);
trans.get_extension(preExt);
if (preExt->get_creator() == this) {
// clean-up the extension if this is the original creator
delete preExt;
trans.set_extension((tlm_recording_extension*) NULL);
} else {
preExt->txHandle=preTx;
}
tgd.set_response_status(trans.get_response_status());
h.record_attribute("trans", tgd);
h.record_attribute("trans.data_value", *(uint64_t*)trans.get_data_ptr());
if(extensionRecording) extensionRecording->recordEndTx(h, trans);
// End the transaction
b_trHandle[trans.get_command()]->end_transaction(h, delay.value(), sc_time_stamp());
// and now the stuff for the timed tx
if (enableTimed.value){
#ifdef DEBUG
cout<<"notify removal of parent with id "<<req->id<<" to btx_handle_map with delay "<<delay<<std::endl;
#endif
b_timed_peq.notify(*req, tlm::END_RESP, delay);
}
}
template< typename TYPES>
void tlm2_recorder<TYPES>::btx_cb(tlm_recording_payload& rec_parts, const typename TYPES::tlm_phase_type& phase) {
scv_tr_handle h;
if (b_trTimedHandle[0] == NULL) {
std::string basename(this->name());
b_streamHandleTimed = new scv_tr_stream((basename + ".blocking_annotated").c_str(), "TRANSACTOR", m_db);
b_trTimedHandle[0] = new scv_tr_generator<tlm::tlm_command, tlm::tlm_response_status>("read", *b_streamHandleTimed);
b_trTimedHandle[1] = new scv_tr_generator<tlm::tlm_command, tlm::tlm_response_status>("write", *b_streamHandleTimed);
b_trTimedHandle[2] = new scv_tr_generator<tlm::tlm_command, tlm::tlm_response_status>("ignore", *b_streamHandleTimed);
}
// Now process outstanding recordings
switch (phase) {
case tlm::BEGIN_REQ:
{
tlm_gp_data tgd(rec_parts);
h = b_trTimedHandle[rec_parts.get_command()]->begin_transaction(rec_parts.get_command());
h.record_attribute("trans", tgd);
h.add_relation(rel_str(PARENT_CHILD), rec_parts.parent);
#ifdef DEBUG
cout<<"adding parent with id "<<rec_parts.id<<" to btx_handle_map"<<std::endl;
#endif
btx_handle_map[rec_parts.id] = h;
}
break;
case tlm::END_RESP: {
#ifdef DEBUG
cout<<"retrieving parent with id "<<rec_parts.id<<" from btx_handle_map"<<std::endl;
#endif
std::map<uint64, scv_tr_handle>::iterator it = btx_handle_map.find(rec_parts.id);
sc_assert(it != btx_handle_map.end());
h = it->second;
btx_handle_map.erase(it);
h.end_transaction(h, rec_parts.get_response_status());
rec_parts.release();
}
break;
default:
sc_assert(!"phase not supported!");
}
return;
}
template< typename TYPES>
tlm::tlm_sync_enum tlm2_recorder<TYPES>::nb_transport_fw(
typename TYPES::tlm_payload_type& trans,
typename TYPES::tlm_phase_type& phase,
sc_core::sc_time& delay) {
if (!isRecordingEnabled())
return fw_port->nb_transport_fw(trans, phase, delay);
// initialize stream and generator if not yet done
if (nb_streamHandle[FW] == NULL) {
string basename(this->name());
nb_streamHandle[FW] = new scv_tr_stream((basename + ".nb_forward").c_str(), "TRANSACTOR", m_db);
nb_fw_trHandle[tlm::TLM_READ_COMMAND] = new scv_tr_generator<tlm::tlm_phase_enum, tlm::tlm_sync_enum>("read", *nb_streamHandle[FW], "tlm_phase", "tlm_sync");
nb_fw_trHandle[tlm::TLM_WRITE_COMMAND] = new scv_tr_generator<tlm::tlm_phase_enum, tlm::tlm_sync_enum>("write", *nb_streamHandle[FW], "tlm_phase", "tlm_sync");
nb_fw_trHandle[tlm::TLM_IGNORE_COMMAND] = new scv_tr_generator<tlm::tlm_phase_enum, tlm::tlm_sync_enum>("ignore", *nb_streamHandle[FW], "tlm_phase", "tlm_sync");
}
/*************************************************************************
* prepare recording
*************************************************************************/
// Get a handle for the new transaction
scv_tr_handle h = nb_fw_trHandle[trans.get_command()]->begin_transaction((tlm::tlm_phase_enum) (unsigned) phase);
tlm_recording_extension* preExt = NULL;
trans.get_extension(preExt);
if (phase == tlm::BEGIN_REQ && preExt == NULL) { // we are the first recording this transaction
preExt = new tlm_recording_extension(h, this);
trans.set_extension(preExt);
} else if (preExt != NULL) {
// link handle if we have a predecessor
h.add_relation(rel_str(PREDECESSOR_SUCCESSOR), preExt->txHandle);
} else {
sc_assert(preExt!=NULL && "ERROR on forward path in phase other than tlm::BEGIN_REQ");
}
// update the extension
preExt->txHandle = h;
h.record_attribute("delay", delay.to_string());
if(extensionRecording) extensionRecording->recordBeginTx(h, trans);
tlm_gp_data tgd(trans);
/*************************************************************************
* do the timed notification
*************************************************************************/
if (enableTimed.value) {
tlm_recording_payload* req = mm->allocate();
req->acquire();
(*req) = trans;
req->parent = h;
nb_timed_peq.notify(*req, phase, delay);
}
/*************************************************************************
* do the access
*************************************************************************/
tlm::tlm_sync_enum status = fw_port->nb_transport_fw(trans, phase, delay);
/*************************************************************************
* handle recording
*************************************************************************/
tgd.set_response_status(trans.get_response_status());
h.record_attribute("trans", tgd);
if(extensionRecording) extensionRecording->recordEndTx(h, trans);
h.record_attribute("tlm_phase[return_path]", (tlm::tlm_phase_enum) (unsigned) phase);
h.record_attribute("delay[return_path]", delay.to_string());
// get the extension and free the memory if it was mine
if (status == tlm::TLM_COMPLETED || (status == tlm::TLM_ACCEPTED && phase == tlm::END_RESP)) {
trans.get_extension(preExt);
if (preExt->get_creator() == this) {
delete preExt;
trans.set_extension((tlm_recording_extension*) NULL);
}
/*************************************************************************
* do the timed notification if req. finished here
*************************************************************************/
tlm_recording_payload* req = mm->allocate();
req->acquire();
(*req) = trans;
req->parent = h;
nb_timed_peq.notify(*req, (status == tlm::TLM_COMPLETED && phase == tlm::BEGIN_REQ) ? tlm::END_RESP : phase,
delay);
} else if (status == tlm::TLM_UPDATED) {
tlm_recording_payload* req = mm->allocate();
req->acquire();
(*req) = trans;
req->parent = h;
nb_timed_peq.notify(*req, phase, delay);
}
// End the transaction
nb_fw_trHandle[trans.get_command()]->end_transaction(h, status);
return status;
}
template< typename TYPES>
tlm::tlm_sync_enum tlm2_recorder<TYPES>::nb_transport_bw(typename TYPES::tlm_payload_type& trans, typename TYPES::tlm_phase_type& phase,
sc_core::sc_time& delay) {
if (!isRecordingEnabled())
return bw_port->nb_transport_bw(trans, phase, delay);
if (nb_streamHandle[BW] == NULL) {
string basename(this->name());
nb_streamHandle[BW] = new scv_tr_stream((basename + ".nb_backward").c_str(), "TRANSACTOR", m_db);
nb_bw_trHandle[0] = new scv_tr_generator<tlm::tlm_phase_enum, tlm::tlm_sync_enum>("read", *nb_streamHandle[BW], "tlm_phase", "tlm_sync");
nb_bw_trHandle[1] = new scv_tr_generator<tlm::tlm_phase_enum, tlm::tlm_sync_enum>("write", *nb_streamHandle[BW], "tlm_phase", "tlm_sync");
nb_bw_trHandle[2] = new scv_tr_generator<tlm::tlm_phase_enum, tlm::tlm_sync_enum>("ignore", *nb_streamHandle[BW], "tlm_phase", "tlm_sync");
}
/*************************************************************************
* prepare recording
*************************************************************************/
tlm_recording_extension* preExt = NULL;
trans.get_extension(preExt);
sc_assert(preExt!=NULL && "ERROR on backward path");
// Get a handle for the new transaction
scv_tr_handle h = nb_bw_trHandle[trans.get_command()]->begin_transaction((tlm::tlm_phase_enum) (unsigned) phase);
// link handle if we have a predecessor and that's not ourself
h.add_relation(rel_str(PREDECESSOR_SUCCESSOR), preExt->txHandle);
// and set the extension handle to this transaction
preExt->txHandle = h;
h.record_attribute("delay", delay.to_string());
if(extensionRecording) extensionRecording->recordBeginTx(h, trans);
tlm_gp_data tgd(trans);
/*************************************************************************
* do the timed notification
*************************************************************************/
if (enableTimed.value) {
tlm_recording_payload* req = mm->allocate();
req->acquire();
(*req) = trans;
req->parent = h;
nb_timed_peq.notify(*req, phase, delay);
}
/*************************************************************************
* do the access
*************************************************************************/
tlm::tlm_sync_enum status = bw_port->nb_transport_bw(trans, phase, delay);
/*************************************************************************
* handle recording
*************************************************************************/
tgd.set_response_status(trans.get_response_status());
h.record_attribute("trans", tgd);
if(extensionRecording) extensionRecording->recordEndTx(h, trans);
// phase and delay are already recorded
h.record_attribute("phase_upd", (tlm::tlm_phase_enum) (unsigned) phase);
h.record_attribute("delay_upd", delay.to_string());
// End the transaction
nb_bw_trHandle[trans.get_command()]->end_transaction(h, status);
if (status == tlm::TLM_COMPLETED || (status == tlm::TLM_UPDATED && phase == tlm::END_RESP)) {
// the transaction is finished
trans.get_extension(preExt);
if (preExt->get_creator() == this) {
// clean-up the extension if this is the original creator
delete preExt;
trans.set_extension((tlm_recording_extension*) NULL);
}
/*************************************************************************
* do the timed notification if req. finished here
*************************************************************************/
tlm_recording_payload* req = mm->allocate();
req->acquire();
(*req) = trans;
req->parent = h;
nb_timed_peq.notify(*req, phase, delay);
}
return status;
}
template< typename TYPES>
void tlm2_recorder<TYPES>::nbtx_cb(tlm_recording_payload& rec_parts, const typename TYPES::tlm_phase_type& phase) {
scv_tr_handle h;
std::map<uint64, scv_tr_handle>::iterator it;
if (nb_streamHandleTimed[FW] == NULL) {
std::string basename(this->name());
nb_streamHandleTimed[FW] = new scv_tr_stream((basename + ".nb_request_annotated").c_str(), "TRANSACTOR", m_db);
nb_txReqHandle[0] = new scv_tr_generator<>("read", *nb_streamHandleTimed[FW]);
nb_txReqHandle[1] = new scv_tr_generator<>("write", *nb_streamHandleTimed[FW]);
nb_txReqHandle[2] = new scv_tr_generator<>("ignore", *nb_streamHandleTimed[FW]);
}
if (nb_streamHandleTimed[BW] == NULL) {
std::string basename(this->name());
nb_streamHandleTimed[BW] = new scv_tr_stream((basename + ".nb_response_annotated").c_str(), "TRANSACTOR", m_db);
nb_txRespHandle[0] = new scv_tr_generator<>("read", *nb_streamHandleTimed[BW]);
nb_txRespHandle[1] = new scv_tr_generator<>("write", *nb_streamHandleTimed[BW]);
nb_txRespHandle[2] = new scv_tr_generator<>("ignore", *nb_streamHandleTimed[BW]);
}
tlm_gp_data tgd(rec_parts);
switch (phase) { // Now process outstanding recordings
case tlm::BEGIN_REQ:
h = nb_txReqHandle[rec_parts.get_command()]->begin_transaction();
h.record_attribute("trans", tgd);
h.add_relation(rel_str(PARENT_CHILD), rec_parts.parent);
#ifdef NBDEBUG
cout<<"adding parent with id "<<rec_parts.id<<" to nbtx_req_handle_map"<<std::endl;
#endif
nbtx_req_handle_map[rec_parts.id] = h;
break;
case tlm::END_REQ:
#ifdef NBDEBUG
cout<<"retrieving parent with id "<<rec_parts.id<<" from nbtx_req_handle_map"<<std::endl;
#endif
it = nbtx_req_handle_map.find(rec_parts.id);
sc_assert(it != nbtx_req_handle_map.end());
h = it->second;
nbtx_req_handle_map.erase(it);
h.end_transaction();
nbtx_last_req_handle_map[rec_parts.id] = h;
break;
case tlm::BEGIN_RESP:
#ifdef NBDEBUG
cout<<"retrieving parent with id "<<rec_parts.id<<" from nbtx_req_handle_map"<<std::endl;
#endif
it = nbtx_req_handle_map.find(rec_parts.id);
if (it != nbtx_req_handle_map.end()) {
h = it->second;
nbtx_req_handle_map.erase(it);
h.end_transaction();
nbtx_last_req_handle_map[rec_parts.id] = h;
}
h = nb_txRespHandle[rec_parts.get_command()]->begin_transaction();
h.record_attribute("trans", tgd);
h.add_relation(rel_str(PARENT_CHILD), rec_parts.parent);
nbtx_req_handle_map[rec_parts.id] = h;
it = nbtx_last_req_handle_map.find(rec_parts.id);
if (it != nbtx_last_req_handle_map.end()) {
scv_tr_handle pred = it->second;
nbtx_last_req_handle_map.erase(it);
h.add_relation(rel_str(PREDECESSOR_SUCCESSOR), pred);
}
break;
case tlm::END_RESP:
#ifdef NBDEBUG
cout<<"retrieving parent with id "<<rec_parts.id<<" from nbtx_req_handle_map"<<std::endl;
#endif
it = nbtx_req_handle_map.find(rec_parts.id);
if (it != nbtx_req_handle_map.end()) {
h = it->second;
nbtx_req_handle_map.erase(it);
h.end_transaction();
}
break;
default:
sc_assert(!"phase not supported!");
}
rec_parts.release();
return;
}
template< typename TYPES>
bool tlm2_recorder<TYPES>::get_direct_mem_ptr(typename TYPES::tlm_payload_type& trans, tlm::tlm_dmi& dmi_data) {
if (!isRecordingEnabled()) {
return fw_port->get_direct_mem_ptr(trans, dmi_data);
}
if (dmi_streamHandle == NULL) {
string basename(this->name());
dmi_streamHandle = new scv_tr_stream((basename + ".dmi").c_str(), "TRANSACTOR", m_db);
dmi_trGetHandle = new scv_tr_generator<tlm_gp_data, tlm_dmi_data>("get_dmi_ptr", *b_streamHandle, "trans",
"dmi_data");
dmi_trInvalidateHandle = new scv_tr_generator<sc_dt::uint64, sc_dt::uint64>("invalidate", *b_streamHandle, "start_delay",
"end_delay");
}
scv_tr_handle h = dmi_trGetHandle->begin_transaction(tlm_gp_data(trans));
bool status= fw_port->get_direct_mem_ptr(trans, dmi_data);
dmi_trGetHandle->end_transaction(h, tlm_dmi_data(dmi_data));
return status;
}
/*! \brief The direct memory interface backward function
*
* This type of transaction is just forwarded and not recorded.
* \param start_addr is the start address of the memory area being invalid
* \param end_addr is the end address of the memory area being invalid
*/
template< typename TYPES>
void tlm2_recorder<TYPES>::invalidate_direct_mem_ptr(sc_dt::uint64 start_addr, sc_dt::uint64 end_addr) {
if (!isRecordingEnabled()) {
bw_port->invalidate_direct_mem_ptr(start_addr, end_addr);
return;
}
if (dmi_streamHandle == NULL) {
string basename(this->name());
dmi_streamHandle = new scv_tr_stream((basename + ".dmi").c_str(), "TRANSACTOR", m_db);
dmi_trGetHandle = new scv_tr_generator<tlm_gp_data, tlm_dmi_data>("get_dmi_ptr", *b_streamHandle, "trans",
"dmi_data");
dmi_trInvalidateHandle = new scv_tr_generator<sc_dt::uint64, sc_dt::uint64>("invalidate", *b_streamHandle, "start_delay",
"end_delay");
}
scv_tr_handle h = dmi_trInvalidateHandle->begin_transaction(start_addr);
bw_port->invalidate_direct_mem_ptr(start_addr, end_addr);
dmi_trInvalidateHandle->end_transaction(h, end_addr);
return;
}
/*! \brief The debug transportfunction
*
* This type of transaction is just forwarded and not recorded.
* \param trans is the generic payload of the transaction
* \return the sync state of the transaction
*/
template< typename TYPES>
unsigned int tlm2_recorder<TYPES>::transport_dbg(typename TYPES::tlm_payload_type& trans) {
unsigned int count = fw_port->transport_dbg(trans);
return count;
}
} // namespace
#endif /* TLM2_RECORDER_H_ */