Added tlm3 recording sockets

This commit is contained in:
Eyck Jentzsch 2015-11-21 19:03:50 +01:00
parent c02f8a6520
commit b083094cc8
7 changed files with 1792 additions and 0 deletions

View File

@ -0,0 +1,722 @@
/*
* 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_ */

View File

@ -0,0 +1,61 @@
/*
* tlm_recorder.h
*
* Created on: 07.11.2015
* Author: eyck
*/
#ifndef TLM2_RECORDER_MODULE_H_
#define TLM2_RECORDER_MODULE_H_
#include <tlm_utils/simple_initiator_socket.h>
#include <tlm_utils/simple_target_socket.h>
#include "tlm2_recorder.h"
namespace scv4tlm {
/*! \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.
* The transaction recorder is simply bound between an existing pair of initiator and target sockets
*/
template<unsigned int BUSWIDTH = 32, typename TYPES=tlm::tlm_base_protocol_types>
class tlm2_recorder_module: public sc_core::sc_module, public tlm2_recorder<TYPES> {
public:
SC_HAS_PROCESS(tlm2_recorder_module);
//! The target socket of the recorder to be bound to the initiator
tlm::tlm_target_socket<BUSWIDTH, TYPES, 1> target;
//! The initiator to be bound to the target socket
tlm::tlm_initiator_socket<BUSWIDTH, TYPES, 1> initiator;
/*! \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_module(sc_core::sc_module_name name, bool recording_enabled = true, scv_tr_db* tr_db = scv_tr_db::get_default_db())
: sc_module(name)
, tlm2_recorder<TYPES>(recording_enabled, tr_db)
{
// bind the sockets to the module
target.bind(*this);
initiator.bind(*this);
}
virtual ~tlm2_recorder_module(){}
virtual tlm::tlm_fw_transport_if<tlm::tlm_base_protocol_types>* get_fw_if() {
return initiator.get_base_port().operator->();
}
virtual tlm::tlm_bw_transport_if<tlm::tlm_base_protocol_types>* get_bw_if() {
return target.get_base_port().operator->();
}
};
} // namespace
#endif /* TLM2_RECORDER_MODULE_H_ */

View File

@ -0,0 +1,557 @@
/*
* tlm_gp_data.h
*
* Created on: 07.11.2015
* Author: eyck
*/
#ifndef TLM_GP_DATA_H_
#define TLM_GP_DATA_H_
#include <tlm>
#include <assert.h>
namespace scv4tlm {
struct tlm_gp_data {
//---------------
// Constructors
//---------------
// Default constructor
tlm_gp_data()
: address(0)
, command(tlm::TLM_IGNORE_COMMAND)
, data(0)
, data_length(0)
, response_status(tlm::TLM_INCOMPLETE_RESPONSE)
, dmi(false)
, byte_enable(0)
, byte_enable_length(0)
, streaming_width(0)
, gp_option(tlm::TLM_MIN_PAYLOAD)
, m_extensions(tlm::max_num_extensions())
, m_ref_count(0)
{
}
explicit tlm_gp_data(tlm::tlm_mm_interface* mm)
: address(0)
, command(tlm::TLM_IGNORE_COMMAND)
, data(0)
, data_length(0)
, response_status(tlm::TLM_INCOMPLETE_RESPONSE)
, dmi(false)
, byte_enable(0)
, byte_enable_length(0)
, streaming_width(0)
, gp_option(tlm::TLM_MIN_PAYLOAD)
, m_extensions(tlm::max_num_extensions())
, m_ref_count(0) {
}
int get_ref_count() const {
return m_ref_count;
}
void reset() {
//should the other members be reset too?
gp_option = tlm::TLM_MIN_PAYLOAD;
m_extensions.free_entire_cache();
}
;
tlm_gp_data(const tlm::tlm_generic_payload& x)
: address(x.get_address())
, command(x.get_command())
, data(x.get_data_ptr())
, data_length(x.get_data_length())
, response_status(x.get_response_status())
, dmi(x.is_dmi_allowed())
, byte_enable(x.get_byte_enable_ptr())
, byte_enable_length(x.get_byte_enable_length())
, streaming_width(x.get_streaming_width())
, gp_option(x.get_gp_option())
, m_extensions(tlm::max_num_extensions())
, m_ref_count(0)
{
}
private:
//disabled copy ctor and assignment operator.
// Copy constructor
tlm_gp_data(const tlm_gp_data& x)
: address(x.get_address())
, command(x.get_command())
, data(x.get_data_ptr())
, data_length(x.get_data_length())
, response_status(x.get_response_status())
, dmi(x.is_dmi_allowed())
, byte_enable(x.get_byte_enable_ptr())
, byte_enable_length(x.get_byte_enable_length())
, streaming_width(x.get_streaming_width())
, gp_option(x.gp_option)
, m_extensions(tlm::max_num_extensions())
, m_ref_count(0)
{
}
public:
// Assignment operator needed for SCV introspection
tlm_gp_data& operator=(const tlm_gp_data& x) {
command = x.get_command();
address = x.get_address();
data = x.get_data_ptr();
data_length = x.get_data_length();
response_status = x.get_response_status();
byte_enable = x.get_byte_enable_ptr();
byte_enable_length = x.get_byte_enable_length();
streaming_width = x.get_streaming_width();
gp_option = x.get_gp_option();
dmi = x.is_dmi_allowed();
// extension copy: all extension arrays must be of equal size by
// construction (i.e. it must either be constructed after C++
// static construction time, or the resize_extensions() method must
// have been called prior to using the object)
for (unsigned int i = 0; i < m_extensions.size(); i++) {
m_extensions[i] = x.get_extension(i);
}
return (*this);
}
// non-virtual deep-copying of the object
void deep_copy_from(const scv4tlm::tlm_gp_data & other) {
command = other.get_command();
address = other.get_address();
data_length = other.get_data_length();
response_status = other.get_response_status();
byte_enable_length = other.get_byte_enable_length();
streaming_width = other.get_streaming_width();
gp_option = other.get_gp_option();
dmi = other.is_dmi_allowed();
// deep copy data
// there must be enough space in the target transaction!
if (data && other.get_data_ptr()) {
memcpy(data, other.get_data_ptr(), data_length);
}
// deep copy byte enables
// there must be enough space in the target transaction!
if (byte_enable && other.get_byte_enable_ptr()) {
memcpy(byte_enable, other.get_byte_enable_ptr(), byte_enable_length);
}
// deep copy extensions (sticky and non-sticky)
for (unsigned int i = 0; i < other.m_extensions.size(); i++) {
if (other.get_extension(i)) { //original has extension i
if (!m_extensions[i]) { //We don't: clone.
tlm::tlm_extension_base *ext = other.get_extension(i)->clone();
if (ext) //extension may not be clonable.
{
set_extension(i, ext);
}
} else { //We already have such extension. Copy original over it.
m_extensions[i]->copy_from(*other.get_extension(i));
}
}
}
}
// To update the state of the original generic payload from a deep copy
// Assumes that "other" was created from the original by calling deep_copy_from
// Argument use_byte_enable_on_read determines whether to use or ignores byte enables
// when copying back the data array on a read command
void update_original_from(const tlm::tlm_generic_payload & other, bool use_byte_enable_on_read = true) {
// Copy back extensions that are present on the original
// update_extensions_from(other);
// Copy back the response status and DMI hint attributes
response_status = other.get_response_status();
dmi = other.is_dmi_allowed();
// Copy back the data array for a read command only
// deep_copy_from allowed null pointers, and so will we
// We assume the arrays are the same size
// We test for equal pointers in case the original and the copy share the same array
if (is_read() && data && other.get_data_ptr() && data != other.get_data_ptr()) {
if (byte_enable && use_byte_enable_on_read) {
if (byte_enable_length == 8 && data_length % 8 == 0) {
// Optimized implementation copies 64-bit words by masking
for (unsigned int i = 0; i < data_length; i += 8) {
typedef sc_dt::uint64* u;
*reinterpret_cast<u>(&data[i]) &= ~*reinterpret_cast<u>(byte_enable);
*reinterpret_cast<u>(&data[i]) |= *reinterpret_cast<u>(&other.get_data_ptr()[i])
& *reinterpret_cast<u>(byte_enable);
}
} else if (byte_enable_length == 4 && data_length % 4 == 0) {
// Optimized implementation copies 32-bit words by masking
for (unsigned int i = 0; i < data_length; i += 4) {
typedef unsigned int* u;
*reinterpret_cast<u>(&data[i]) &= ~*reinterpret_cast<u>(byte_enable);
*reinterpret_cast<u>(&data[i]) |= *reinterpret_cast<u>(&other.get_data_ptr()[i])
& *reinterpret_cast<u>(byte_enable);
}
} else
// Unoptimized implementation
for (unsigned int i = 0; i < data_length; i++)
if (byte_enable[i % byte_enable_length])
data[i] = other.get_data_ptr()[i];
} else
memcpy(data, other.get_data_ptr(), data_length);
}
}
/*
void update_extensions_from(const tlm::tlm_generic_payload & other) {
// deep copy extensions that are already present
for (unsigned int i = 0; i < tlm::max_num_extensions(); i++) {
if (other.get_extension(i)) { //original has extension i
if (m_extensions[i]) { //We have it too. copy.
m_extensions[i]->copy_from(*other.get_extension(i));
}
}
}
}
*/
// Free all extensions. Useful when reusing a cloned transaction that doesn't have memory manager.
// normal and sticky extensions are freed and extension array cleared.
void free_all_extensions() {
m_extensions.free_entire_cache();
for (unsigned int i = 0; i < m_extensions.size(); i++) {
if (m_extensions[i]) {
m_extensions[i]->free();
m_extensions[i] = 0;
}
}
}
//--------------
// Destructor
//--------------
virtual ~tlm_gp_data() {
for(unsigned int i=0; i<m_extensions.size(); i++)
if(m_extensions[i]) m_extensions[i]->free();
}
//----------------
// API (including setters & getters)
//---------------
// Command related method
bool is_read() const {
return (command == tlm::TLM_READ_COMMAND);
}
void set_read() {
command = tlm::TLM_READ_COMMAND;
}
bool is_write() const {
return (command == tlm::TLM_WRITE_COMMAND);
}
void set_write() {
command = tlm::TLM_WRITE_COMMAND;
}
tlm::tlm_command get_command() const {
return command;
}
void set_command(const tlm::tlm_command command) {
this->command = command;
}
// Address related methods
sc_dt::uint64 get_address() const {
return address;
}
void set_address(const sc_dt::uint64 address) {
this->address = address;
}
// Data related methods
unsigned char* get_data_ptr() const {
return data;
}
void set_data_ptr(unsigned char* data) {
this->data = data;
}
// Transaction length (in bytes) related methods
unsigned int get_data_length() const {
return data_length;
}
void set_data_length(const unsigned int length) {
data_length = length;
}
// Response status related methods
bool is_response_ok() const {
return (response_status > 0);
}
bool is_response_error() const {
return (response_status <= 0);
}
tlm::tlm_response_status get_response_status() const {
return response_status;
}
void set_response_status(const tlm::tlm_response_status response_status) {
this->response_status = response_status;
}
std::string get_response_string() const {
switch (response_status) {
case tlm::TLM_OK_RESPONSE:
return "TLM_OK_RESPONSE";
case tlm::TLM_INCOMPLETE_RESPONSE:
return "TLM_INCOMPLETE_RESPONSE";
case tlm::TLM_GENERIC_ERROR_RESPONSE:
return "TLM_GENERIC_ERROR_RESPONSE";
case tlm::TLM_ADDRESS_ERROR_RESPONSE:
return "TLM_ADDRESS_ERROR_RESPONSE";
case tlm::TLM_COMMAND_ERROR_RESPONSE:
return "TLM_COMMAND_ERROR_RESPONSE";
case tlm::TLM_BURST_ERROR_RESPONSE:
return "TLM_BURST_ERROR_RESPONSE";
case tlm::TLM_BYTE_ENABLE_ERROR_RESPONSE:
return "TLM_BYTE_ENABLE_ERROR_RESPONSE";
}
return "TLM_UNKNOWN_RESPONSE";
}
// Streaming related methods
unsigned int get_streaming_width() const {
return streaming_width;
}
void set_streaming_width(const unsigned int streaming_width) {
this->streaming_width = streaming_width;
}
// Byte enable related methods
unsigned char* get_byte_enable_ptr() const {
return byte_enable;
}
void set_byte_enable_ptr(unsigned char* byte_enable) {
this->byte_enable = byte_enable;
}
unsigned int get_byte_enable_length() const {
return byte_enable_length;
}
void set_byte_enable_length(const unsigned int byte_enable_length) {
this->byte_enable_length = byte_enable_length;
}
// This is the "DMI-hint" a slave can set this to true if it
// wants to indicate that a DMI request would be supported:
void set_dmi_allowed(bool dmi_allowed) {
dmi = dmi_allowed;
}
bool is_dmi_allowed() const {
return dmi;
}
// Use full set of attributes in DMI/debug?
tlm::tlm_gp_option get_gp_option() const {
return gp_option;
}
void set_gp_option(const tlm::tlm_gp_option gp_opt) {
gp_option = gp_opt;
}
public:
/* --------------------------------------------------------------------- */
/* Generic Payload attributes: */
/* --------------------------------------------------------------------- */
/* - m_command : Type of transaction. Three values supported: */
/* - TLM_WRITE_COMMAND */
/* - TLM_READ_COMMAND */
/* - TLM_IGNORE_COMMAND */
/* - m_address : Transaction base address (byte-addressing). */
/* - m_data : When m_command = TLM_WRITE_COMMAND contains a */
/* pointer to the data to be written in the target.*/
/* When m_command = TLM_READ_COMMAND contains a */
/* pointer where to copy the data read from the */
/* target. */
/* - m_length : Total number of bytes of the transaction. */
/* - m_response_status : This attribute indicates whether an error has */
/* occurred during the transaction. */
/* Values supported are: */
/* - TLM_OK_RESP */
/* - TLM_INCOMPLETE_RESP */
/* - TLM_GENERIC_ERROR_RESP */
/* - TLM_ADDRESS_ERROR_RESP */
/* - TLM_COMMAND_ERROR_RESP */
/* - TLM_BURST_ERROR_RESP */
/* - TLM_BYTE_ENABLE_ERROR_RESP */
/* */
/* - m_byte_enable : It can be used to create burst transfers where */
/* the address increment between each beat is greater */
/* than the word length of each beat, or to place */
/* words in selected byte lanes of a bus. */
/* - m_byte_enable_length : For a read or a write command, the target */
/* interpret the byte enable length attribute as the */
/* number of elements in the bytes enable array. */
/* - m_streaming_width : */
/* --------------------------------------------------------------------- */
sc_dt::uint64 address;
tlm::tlm_command command;
unsigned char* data;
unsigned int data_length;
tlm::tlm_response_status response_status;
bool dmi;
unsigned char* byte_enable;
unsigned int byte_enable_length;
unsigned int streaming_width;
tlm::tlm_gp_option gp_option;
public:
/* --------------------------------------------------------------------- */
/* Dynamic extension mechanism: */
/* --------------------------------------------------------------------- */
/* The extension mechanism is intended to enable initiator modules to */
/* optionally and transparently add data fields to the */
/* tlm_generic_payload. Target modules are free to check for extensions */
/* and may or may not react to the data in the extension fields. The */
/* definition of the extensions' semantics is solely in the */
/* responsibility of the user. */
/* */
/* The following rules apply: */
/* */
/* - Every extension class must be derived from tlm_extension, e.g.: */
/* class my_extension : public tlm_extension<my_extension> { ... } */
/* */
/* - A tlm_generic_payload object should be constructed after C++ */
/* static initialization time. This way it is guaranteed that the */
/* extension array is of sufficient size to hold all possible */
/* extensions. Alternatively, the initiator module can enforce a valid */
/* extension array size by calling the resize_extensions() method */
/* once before the first transaction with the payload object is */
/* initiated. */
/* */
/* - Initiators should use the the set_extension(e) or clear_extension(e)*/
/* methods for manipulating the extension array. The type of the */
/* argument must be a pointer to the specific registered extension */
/* type (my_extension in the above example) and is used to */
/* automatically locate the appropriate index in the array. */
/* */
/* - Targets can check for a specific extension by calling */
/* get_extension(e). e will point to zero if the extension is not */
/* present. */
/* */
/* --------------------------------------------------------------------- */
// Stick the pointer to an extension into the vector, return the
// previous value:
template<typename T> T* set_extension(T* ext) {
return static_cast<T*>(set_extension(T::ID, ext));
}
// non-templatized version with manual index:
tlm::tlm_extension_base* set_extension(unsigned int index, tlm::tlm_extension_base* ext) {
tlm::tlm_extension_base* tmp = m_extensions[index];
m_extensions[index] = ext;
return tmp;
}
// Check for an extension, ext will point to 0 if not present
template<typename T> void get_extension(T*& ext) const {
ext = get_extension<T>();
}
template<typename T> T* get_extension() const {
return static_cast<T*>(get_extension(T::ID));
}
// Non-templatized version with manual index:
tlm::tlm_extension_base* get_extension(unsigned int index) const {
return m_extensions[index];
}
//this call just removes the extension from the txn but does not
// call free() or tells the MM to do so
// it return false if there was active MM so you are now in an unsafe situation
// recommended use: when 100% sure there is no MM
template<typename T> void clear_extension(const T* ext) {
clear_extension<T>();
}
//this call just removes the extension from the txn but does not
// call free() or tells the MM to do so
// it return false if there was active MM so you are now in an unsafe situation
// recommended use: when 100% sure there is no MM
template<typename T> void clear_extension() {
clear_extension(T::ID);
}
//this call removes the extension from the txn and does
// call free() or tells the MM to do so when the txn is finally done
// recommended use: when not sure there is no MM
template<typename T> void release_extension(T* ext) {
release_extension<T>();
}
//this call removes the extension from the txn and does
// call free() or tells the MM to do so when the txn is finally done
// recommended use: when not sure there is no MM
template<typename T> void release_extension() {
release_extension(T::ID);
}
private:
// Non-templatized version with manual index
void clear_extension(unsigned int index) {
m_extensions[index] = static_cast<tlm::tlm_extension_base*>(0);
}
// Non-templatized version with manual index
void release_extension(unsigned int index) {
m_extensions[index]->free();
m_extensions[index] = static_cast<tlm::tlm_extension_base*>(0);
}
public:
// Make sure the extension array is large enough. Can be called once by
// an initiator module (before issuing the first transaction) to make
// sure that the extension array is of correct size. This is only needed
// if the initiator cannot guarantee that the generic payload object is
// allocated after C++ static construction time.
void resize_extensions() {
m_extensions.expand(tlm::max_num_extensions());
}
private:
tlm::tlm_array<tlm::tlm_extension_base*> m_extensions;
unsigned int m_ref_count;
};
struct tlm_dmi_data {
tlm_dmi_data()
:dmi_ptr(0)
,dmi_start_address(0)
,dmi_end_address(0)
,dmi_access(tlm::tlm_dmi::DMI_ACCESS_NONE)
,dmi_read_latency(0)
,dmi_write_latency(0)
{}
tlm_dmi_data(tlm::tlm_dmi& dmi_data)
:dmi_ptr(dmi_data.get_dmi_ptr())
,dmi_start_address(dmi_data.get_start_address())
,dmi_end_address(dmi_data.get_end_address())
,dmi_access(dmi_data.get_granted_access())
,dmi_read_latency(dmi_data.get_read_latency().value())
,dmi_write_latency(dmi_data.get_write_latency().value())
{}
//--------------
// Destructor
//--------------
virtual ~tlm_dmi_data() {}
unsigned char* dmi_ptr;
sc_dt::uint64 dmi_start_address;
sc_dt::uint64 dmi_end_address;
tlm::tlm_dmi::dmi_access_e dmi_access;
sc_dt::uint64 dmi_read_latency;
sc_dt::uint64 dmi_write_latency;
};
}
#endif /* TLM_GP_DATA_H_ */

View File

@ -0,0 +1,131 @@
/*
* tlm_gp_data_ext.h
*
* Created on: 07.11.2015
* Author: eyck
*/
#ifndef TLM_GP_DATA_EXT_H_
#define TLM_GP_DATA_EXT_H_
#include "scv.h"
#include "tlm_gp_data.h"
template<>
class scv_extensions<tlm::tlm_command> : public scv_enum_base<tlm::tlm_command> {
public:
SCV_ENUM_CTOR(tlm::tlm_command) {
SCV_ENUM(tlm::TLM_READ_COMMAND);
SCV_ENUM(tlm::TLM_WRITE_COMMAND);
SCV_ENUM(tlm::TLM_IGNORE_COMMAND);
}
};
template<>
class scv_extensions<tlm::tlm_response_status> : public scv_enum_base<tlm::tlm_response_status> {
public:
SCV_ENUM_CTOR(tlm::tlm_response_status) {
SCV_ENUM(tlm::TLM_OK_RESPONSE);
SCV_ENUM(tlm::TLM_INCOMPLETE_RESPONSE);
SCV_ENUM(tlm::TLM_GENERIC_ERROR_RESPONSE);
SCV_ENUM(tlm::TLM_ADDRESS_ERROR_RESPONSE);
SCV_ENUM(tlm::TLM_COMMAND_ERROR_RESPONSE);
SCV_ENUM(tlm::TLM_BURST_ERROR_RESPONSE);
SCV_ENUM(tlm::TLM_BYTE_ENABLE_ERROR_RESPONSE);
}
};
template<>
class scv_extensions<tlm::tlm_gp_option> : public scv_enum_base<tlm::tlm_gp_option> {
public:
SCV_ENUM_CTOR(tlm::tlm_gp_option) {
SCV_ENUM(tlm::TLM_MIN_PAYLOAD);
SCV_ENUM(tlm::TLM_FULL_PAYLOAD);
SCV_ENUM(tlm::TLM_FULL_PAYLOAD_ACCEPTED);
}
};
template<>
class scv_extensions<tlm::tlm_phase_enum> : public scv_enum_base<tlm::tlm_phase_enum> {
public:
SCV_ENUM_CTOR(tlm::tlm_phase_enum) {
SCV_ENUM(tlm::UNINITIALIZED_PHASE);
SCV_ENUM(tlm::BEGIN_REQ);
SCV_ENUM(tlm::END_REQ);
SCV_ENUM(tlm::BEGIN_RESP);
SCV_ENUM(tlm::END_RESP);
}
};
template<>
class scv_extensions<tlm::tlm_sync_enum> : public scv_enum_base<tlm::tlm_sync_enum> {
public:
SCV_ENUM_CTOR(tlm::tlm_sync_enum) {
SCV_ENUM(tlm::TLM_ACCEPTED);
SCV_ENUM(tlm::TLM_UPDATED);
SCV_ENUM(tlm::TLM_COMPLETED);
}
};
template<>
class scv_extensions<scv4tlm::tlm_gp_data> : public scv_extensions_base<scv4tlm::tlm_gp_data> {
public:
scv_extensions<sc_dt::uint64> address;
scv_extensions<tlm::tlm_command> command;
scv_extensions<unsigned char*> data;
scv_extensions<unsigned int> data_length;
scv_extensions<tlm::tlm_response_status> response_status;
scv_extensions<bool> dmi;
scv_extensions<unsigned char*> byte_enable;
scv_extensions<unsigned int> byte_enable_length;
scv_extensions<unsigned int> streaming_width;
scv_extensions<tlm::tlm_gp_option> gp_option;
SCV_EXTENSIONS_CTOR(scv4tlm::tlm_gp_data) {
//must be in order
SCV_FIELD(address);
SCV_FIELD(command);
SCV_FIELD(data);
SCV_FIELD(data_length);
SCV_FIELD(response_status);
SCV_FIELD(dmi);
SCV_FIELD(byte_enable);
SCV_FIELD(byte_enable_length);
SCV_FIELD(streaming_width);
SCV_FIELD(gp_option);
}
};
template<>
class scv_extensions<tlm::tlm_dmi::dmi_access_e> : public scv_enum_base<tlm::tlm_dmi::dmi_access_e> {
public:
SCV_ENUM_CTOR(tlm::tlm_dmi::dmi_access_e) {
SCV_ENUM(tlm::tlm_dmi::DMI_ACCESS_NONE);
SCV_ENUM(tlm::tlm_dmi::DMI_ACCESS_READ);
SCV_ENUM(tlm::tlm_dmi::DMI_ACCESS_WRITE);
SCV_ENUM(tlm::tlm_dmi::DMI_ACCESS_READ_WRITE);
}
};
template<>
class scv_extensions<scv4tlm::tlm_dmi_data> : public scv_extensions_base<scv4tlm::tlm_dmi_data> {
public:
scv_extensions<unsigned char*> dmi_ptr;
scv_extensions<sc_dt::uint64> dmi_start_address;
scv_extensions<sc_dt::uint64> dmi_end_address;
scv_extensions<tlm::tlm_dmi::dmi_access_e> dmi_access;
scv_extensions<sc_dt::uint64> dmi_read_latency;
scv_extensions<sc_dt::uint64> dmi_write_latency;
SCV_EXTENSIONS_CTOR(scv4tlm::tlm_dmi_data) {
//must be in order
SCV_FIELD(dmi_ptr);
SCV_FIELD(dmi_start_address);
SCV_FIELD(dmi_end_address);
SCV_FIELD(dmi_access);
SCV_FIELD(dmi_read_latency);
SCV_FIELD(dmi_write_latency);
}
};
#endif /* TLM_GP_DATA_EXT_H_ */

View File

@ -0,0 +1,125 @@
/*
* tlm_rec_target_socket.h
*
* Created on: 08.11.2015
* Author: eyck
*/
#ifndef TLM_REC_INITIATOR_SOCKET_H_
#define TLM_REC_INITIATOR_SOCKET_H_
#include <tlm_core/tlm_2/tlm_sockets/tlm_initiator_socket.h>
#include "tlm2_recorder.h"
namespace scv4tlm {
template<unsigned int BUSWIDTH = 32, typename TYPES = tlm::tlm_base_protocol_types, int N = 1
#if !(defined SYSTEMC_VERSION & SYSTEMC_VERSION <= 20050714)
, sc_core::sc_port_policy POL = sc_core::SC_ONE_OR_MORE_BOUND
#endif
>
class tlm_rec_initiator_socket: public tlm::tlm_initiator_socket<BUSWIDTH
, TYPES
, N
#if !(defined SYSTEMC_VERSION & SYSTEMC_VERSION <= 20050714)
, POL
#endif
> {
static std::string gen_name(const char* first, const char* second){
std::stringstream ss;
ss<<first<<"_"<<second;
return ss.str();
}
public:
typedef tlm::tlm_fw_transport_if<TYPES> fw_interface_type;
typedef tlm::tlm_bw_transport_if<TYPES> bw_interface_type;
typedef sc_core::sc_port<fw_interface_type, N
#if !(defined SYSTEMC_VERSION & SYSTEMC_VERSION <= 20050714)
, POL
#endif
> port_type;
typedef sc_core::sc_export<bw_interface_type> export_type;
typedef tlm::tlm_base_target_socket_b<BUSWIDTH,
fw_interface_type,
bw_interface_type> base_target_socket_type;
typedef tlm::tlm_base_initiator_socket_b<BUSWIDTH,
fw_interface_type,
bw_interface_type> base_type;
tlm_rec_initiator_socket() :
tlm::tlm_initiator_socket<BUSWIDTH
, TYPES
, N
#if !(defined SYSTEMC_VERSION & SYSTEMC_VERSION <= 20050714)
, POL
#endif
>()
,recorder()
{
}
explicit tlm_rec_initiator_socket(const char* name) :
tlm::tlm_initiator_socket<BUSWIDTH
, TYPES
, N
#if !(defined SYSTEMC_VERSION & SYSTEMC_VERSION <= 20050714)
, POL
#endif
>(name)
,recorder(gen_name(name, "rec").c_str())
{
}
virtual ~tlm_rec_initiator_socket(){}
virtual const char* kind() const {
return "tlm_rec_target_socket";
}
//
// Bind initiator socket to target socket
// - Binds the port of the initiator socket to the export of the target
// socket
// - Binds the port of the target socket to the export of the initiator
// socket
//
virtual void bind(base_target_socket_type& s) {
// initiator.port -> target.export
(this->get_base_port())(recorder);
recorder.fw_port(s.get_base_interface());
// target.port -> initiator.export
(s.get_base_port())(recorder);
recorder.bw_port(this->get_base_interface());
}
//
// Bind initiator socket to initiator socket (hierarchical bind)
// - Binds both the export and the port
//
virtual void bind(base_type& s){
// port
(this->get_base_port())(recorder);
recorder.fw_port(s.get_base_port());
// export
(s.get_base_export())(recorder);
recorder.bw_port(this->get_base_export());
}
//
// Bind interface to socket
// - Binds the interface to the export of this socket
//
virtual void bind(bw_interface_type& ifs) {
(this->get_base_export())(ifs);
}
void setExtensionRecording(tlm_extensions_recording_if<TYPES>* extensionRecording){
recorder.setExtensionRecording(extensionRecording);
}
protected:
scv4tlm::tlm2_recorder<TYPES> recorder;
};
}
// namespace scv4tlm
#endif /* TLM_REC_TARGET_SOCKET_H_ */

View File

@ -0,0 +1,119 @@
/*
* tlm_rec_target_socket.h
*
* Created on: 08.11.2015
* Author: eyck
*/
#ifndef TLM_REC_TARGET_SOCKET_H_
#define TLM_REC_TARGET_SOCKET_H_
#include <tlm_core/tlm_2/tlm_sockets/tlm_target_socket.h>
#include "tlm2_recorder.h"
namespace scv4tlm {
template<unsigned int BUSWIDTH = 32, typename TYPES = tlm::tlm_base_protocol_types, int N = 1
#if !(defined SYSTEMC_VERSION & SYSTEMC_VERSION <= 20050714)
, sc_core::sc_port_policy POL = sc_core::SC_ONE_OR_MORE_BOUND
#endif
>
class tlm_rec_target_socket: public tlm::tlm_target_socket<BUSWIDTH
, TYPES
, N
#if !(defined SYSTEMC_VERSION & SYSTEMC_VERSION <= 20050714)
, POL
#endif
> {
static std::string gen_name(const char* first, const char* second){
std::stringstream ss;
ss<<first<<"_"<<second;
return ss.str();
}
public:
typedef tlm::tlm_fw_transport_if<TYPES> fw_interface_type;
typedef tlm::tlm_bw_transport_if<TYPES> bw_interface_type;
typedef sc_core::sc_port<bw_interface_type, N
#if !(defined SYSTEMC_VERSION & SYSTEMC_VERSION <= 20050714)
, POL
#endif
> port_type;
typedef sc_core::sc_export<fw_interface_type> export_type;
typedef tlm::tlm_base_initiator_socket_b<BUSWIDTH, fw_interface_type, bw_interface_type> base_initiator_socket_type;
typedef tlm::tlm_base_target_socket_b<BUSWIDTH, fw_interface_type, bw_interface_type> base_type;
tlm_rec_target_socket() :
tlm::tlm_target_socket<BUSWIDTH
, TYPES
, N
#if !(defined SYSTEMC_VERSION & SYSTEMC_VERSION <= 20050714)
, POL
#endif
>()
,recorder()
{
}
explicit tlm_rec_target_socket(const char* name) :
tlm::tlm_target_socket<BUSWIDTH
, TYPES
, N
#if !(defined SYSTEMC_VERSION & SYSTEMC_VERSION <= 20050714)
, POL
#endif
>(name)
,recorder(gen_name(name, "rec").c_str())
{
}
virtual ~tlm_rec_target_socket(){}
virtual const char* kind() const {
return "tlm_rec_target_socket";
}
//
// Bind target socket to target socket (hierarchical bind)
// - Binds both the export and the port
//
virtual void bind(base_type& s) {
// export
(this->get_base_export())(s.get_base_export()); // will be handled by bind(fw_interface_type& ifs)
// port
(s.get_base_port())(recorder); // bind the recording interface to the port, recording will use the m_port
}
//
// Bind interface to socket
// - Binds the interface to the export
//
virtual void bind(fw_interface_type& ifs){
export_type* exp = &this->get_base_export();
if( this == exp ) {
export_type::bind(recorder); // non-virtual function call
recorder.fw_port(ifs);
recorder.bw_port(this->get_base_port());
} else {
exp->bind( ifs );
}
}
//
// Forward to 'operator->()' of port class
//
bw_interface_type* operator->() {
return &recorder;
}
scv4tlm::tlm2_recorder<TYPES>& get_recorder(){
return recorder;
}
protected:
scv4tlm::tlm2_recorder<TYPES> recorder;
};
}
// namespace scv4tlm
#endif /* TLM_REC_TARGET_SOCKET_H_ */

View File

@ -0,0 +1,77 @@
/*
* tlm2_tx_record_extension.h
*
* Created on: 07.11.2015
* Author: eyck
*/
#ifndef TLM_RECORDING_EXTENSION_H_
#define TLM_RECORDING_EXTENSION_H_
#include <scv.h>
namespace scv4tlm {
//! transaction relationships
enum tx_rel {
PARENT_CHILD = 0, /*!< indicates parent child relationship */
PREDECESSOR_SUCCESSOR /*!< indicates predecessor successor relationship */
};
//! the string representation of the tx_rel
static char const* tx_rel_str[] = { "PARENT/CHILD", "PRED/SUCC" };
/*! \brief cast the tx_rel enum to a string
*
* \param tc_rel is the relationship enum
*/
inline const char* rel_str(tx_rel rel) {
return (tx_rel_str[rel]);
}
/*! \brief generic payload extension class holding the handle of the last recorded SCV transaction
*
* This extension is been used in the \ref scv_tlm2_recorder. The recorder stores the handle to the generated SCV transaction and
* forwrds it along with the generic payload. If the recorder finds an extension containing a valid handle it links the generated
* SCV transdaction to the found one using the \ref PREDECESSOR releationship
*/
struct tlm_recording_extension: public tlm::tlm_extension<tlm_recording_extension> {
/*! \brief clone the given extension and duplicate the SCV transaction handle.
*
*/
virtual tlm_extension_base* clone() const {
tlm_recording_extension* t = new tlm_recording_extension(this->txHandle, this->creator);
return t;
}
/*! \brief copy data between extensions.
*
* \param from is the source extension.
*/
virtual void copy_from(tlm_extension_base const& from) {
txHandle = static_cast<tlm_recording_extension const &>(from).txHandle;
creator = static_cast<tlm_recording_extension const &>(from).creator;
}
/*! \brief constructor storing the handle of the transaction and the owner of this extension
*
* \param handle is the handle of the created SCV transaction.
* \param creator_ is the pointer to the owner of this extension (usually an instance of scv_tlm2_recorder).
*/
tlm_recording_extension(scv_tr_handle handle, void* creator_) :
txHandle(handle), creator(creator_) {
}
/*! \brief accessor to the owner, the property is read only.
*
*/
void* get_creator() {
return creator;
}
/*! \brief accessor to the SCV transaction handle.
*
*/
scv_tr_handle txHandle;
private:
//! the owner of this transaction
void* creator;
};
}
#endif /* TLM_RECORDING_EXTENSION_H_ */