Added tlm3 recording sockets
This commit is contained in:
parent
c02f8a6520
commit
b083094cc8
|
@ -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_ */
|
|
@ -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_ */
|
|
@ -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_ */
|
|
@ -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_ */
|
|
@ -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_ */
|
|
@ -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_ */
|
|
@ -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_ */
|
Loading…
Reference in New Issue