Show shipAPI.h syntax highlighted
/*
Copyright (c) 2006 : Technical University of Braunschweig, Germany
All rights reserved.
Authors: Robert Guenzel, Wolfgang Klingauf, TU Braunschweig, E.I.S.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer. Redistributions
in binary form must reproduce the above copyright notice, this list of
conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution. Neither the name of
the Technical University of Braunschweig, Germany nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __shipAPI_h__
#define __shipAPI_h__
//#define VERBOSE
#include <systemc.h>
#include "gstlm/tlm_slave_if.h"
#include "protocol/generic.h"
using namespace tlm;
///
//---------------------------------------------------------------------------
/**
* This is an abstract class from which objects must inherit to be
* SHIP-transferrable.
*/
//---------------------------------------------------------------------------
class ship_serializable_if
{
public:
/// Serialize the data content of a SHIP object that inherits from ship_serializable_if.
/**
* This method has to be implemented by any SHIP object and
* must return a reference to an array of sc_uint<64>
* values that contain a serialized data representation of
* the content of the SHIP object.
*
* @return Returns the reference to an array of sc_uint<64> values.
*/
virtual sc_uint<64> * serialize() = 0;
/// Deserialize the data content of a SHIP object that inherits from ship_serializable_if.
/**
* This method has to be implemented by any SHIP object.
* Its purpose is to read a serial data stream which was
* generated by the serialize method, and to copy the content
* of this data stream into the corresponding data fields
* of the SHIP object.
*
* @param values An array of sc_uint<64> objects has to be passed
* which contains the data values that are to be deserialized.
* The deserialize method reads those data values and
* maps them to the SHIP object's internal data structure.
* @param length Number of 64 bit values that are passed.
* @return The number of 64 bit values that were successfully processed.
*/
virtual unsigned int deserialize(sc_uint<64> *values, unsigned int length)=0;
/// Return the size of the serialized object
/**
* @return The return value has to reflect the size of
* the data array which is generated by the serialize
* method. The returned value has to be 64 bit aligned.
* Thus, if the SHIP object for example needs a buffer
* of four sc_uint<64> values to store its serial data
* representation, the getSerialLength method should
* return 4.
*/
virtual unsigned int getSerialLength()=0;
};
//---------------------------------------------------------------------------
/**
* SHIP command definitions
* @see shipSlaveAPI.waitEvent()
*/
//---------------------------------------------------------------------------
///The enum for the available commands in the SHIP-system
enum ship_command_enum {
///A master requests data from a slave
SHIP_REQUEST,
///A master sends data to a slave
SHIP_SEND,
///No active command available
SHIP_NONE
};
///This struct is returned by the waitEvent method.
/**
* @see ship_slave_if.waitEvent()
*/
struct ship_command {
///The actual command of this ship-command
/**
* @see command
*/
ship_command_enum cmd;
///The burstlength of the current command.
/**
* This burstlength information may be necessary when
* the serialisation length isn't fixed
*/
int burstlength;
};
//---------------------------------------------------------------------------
/**
* SHIP master API implementation based on the GreenBus TLM fabric.
*/
//---------------------------------------------------------------------------
template <class T>
class shipMasterAPI
: public GenericMasterPort
{
public:
typedef GenericMasterPort PORT;
typedef typename PORT::transaction transaction;
typedef typename PORT::transactionHandle transactionHandle;
typedef typename PORT::accessHandle accessHandle;
typedef typename PORT::phaseHandle phaseHandle;
SC_HAS_PROCESS(shipMasterAPI);
shipMasterAPI ( sc_module_name port_name, bool pvtmode = false ) :
PORT(port_name),
mAddr(0x0),
mPVT(pvtmode)
{
// DUST structure analysis
#ifdef DUST_ENABLE
DUST_MASTER_PORT("ShipMasterAPI", "SHIP");
#endif
SC_METHOD(react); // handle PVT protocol
sensitive << PORT::default_event();
dont_initialize();
mCmd.cmd = SHIP_NONE;
}
shipMasterAPI ( sc_module_name port_name, MAddr target_addr, bool pvtmode = false ) :
PORT(port_name),
mAddr(target_addr),
mPVT(pvtmode)
{
// DUST structure analysis
#ifdef DUST_ENABLE
DUST_MASTER_PORT("ShipMasterAPI", "SHIP");
#endif
SC_METHOD(react);
sensitive << PORT::default_event();
dont_initialize();
mCmd.cmd = SHIP_NONE;
}
/// Send a SHIP object to a slave
/**
* The send method sends a SHIP object to a SHIP slave.
* Communication using this method is blocking, thus
* it will not return until the SHIP object has been
* completely delivered to the slave.
*
* @param obj The object to be send.
*/
void send( T& obj ) {
accessHandle ah = PORT::create_transaction();
ah->set_mCmd(Generic_MCMD_WR);
ah->set_mAddr(mAddr);
unsigned int objbytes = obj.getSerialLength()*8;
mCmd.cmd = SHIP_SEND;
mCmd.burstlength = objbytes/8;
if (!mPVT) { // PV bypass mode
ah->set_mBurstLength(sizeof(T*)); // only 32 bit pointer to SHIP object
ah->set_mData(MasterDataType((unsigned char*)&obj, sizeof(T*)));
transactionHandle th = boost::dynamic_pointer_cast<transaction>(ah);
GS_TRACE(name(), "send() calls PV b_transact.");
PORT::Transact(th);
mCmd.cmd = SHIP_NONE;
}
else { // PVT transaction
sc_uint<64> *objdata = obj.serialize();
ah->set_mBurstLength(objbytes); // serial length is 64 bit aligned
unsigned char *data = new unsigned char[objbytes];
// copy serialized SHIP object to a temporal char array
for (unsigned int i=0, j=0; i<objbytes; i+=8, j++) {
data[i] = objdata[j](63,56);
data[i+1] = objdata[j](55,48);
data[i+2] = objdata[j](47,40);
data[i+3] = objdata[j](39,32);
data[i+4] = objdata[j](31,24);
data[i+5] = objdata[j](23,16);
data[i+6] = objdata[j](15,8);
data[i+7] = objdata[j](7,0);
}
ah->set_mData(MasterDataType(data, objbytes));
transactionHandle th = boost::dynamic_pointer_cast<transaction>(ah);
phaseHandle phase;
// PVT request phase
GS_TRACE(name(), "send() starts request phase.");
PORT::Request.block(th);
phase = PORT::get_phase();
if (phase.state == GenericPhase::RequestAccepted) {
GS_TRACE(name(), "send() request has been accepted by the slave.");
} else {
SC_REPORT_ERROR(name(), "Master request was not acknowledged by the slave - something went wrong.");
mCmd.cmd = SHIP_NONE;
return;
}
// PVT data phase
PORT::SendData.block(th, phase);
phase = PORT::get_phase();
if (phase.state == GenericPhase::DataAccepted) {
GS_TRACE(name(), "send() data has been accepted by the slave.");
} else {
SC_REPORT_ERROR(name(), "send() our data was not accepted by the slave - something went wrong.");
}
mCmd.cmd = SHIP_NONE;
delete[] data;
}
}
/// Request a SHIP object from a slave
/**
* The request method requests a SHIP object from a SHIP slave.
* Communication using this method is blocking, thus the request
* method will not return unless the SHIP object
* has been completely received.
* @param obj A reference to an SHIP object, into which
* the requested data will be copied.
*/
void request( T& obj ) {
sc_assert(mCmd.cmd == SHIP_NONE);
accessHandle ah = PORT::create_transaction();
ah->set_mCmd(Generic_MCMD_RD);
ah->set_mAddr(mAddr);
ah->set_mBurstLength(0); // empty request
mCmd.cmd = SHIP_REQUEST;
transactionHandle th = boost::dynamic_pointer_cast<transaction>(ah);
phaseHandle phase;
if (!mPVT) { // PV bypass mode
GS_TRACE(name(), "request() calls PV b_transact.");
PORT::Transact(th);
// now lets look what we have received
sc_assert(th->get_mBurstLength() == sizeof(T*));
MasterDataType data;
data.set(th->get_mData());
T *robj = ((T*)&data[0]);
obj = *robj; // copy received SHIP object to master
}
else { // PVT mode
GS_TRACE(name(), "request() starts request phase.");
PORT::Request.block(th);
phase = PORT::get_phase();
if (phase.state == GenericPhase::RequestAccepted) {
GS_TRACE(name(), "request() our request has been accepted by the slave.");
} else {
SC_REPORT_ERROR(name(), "request() master request was not acknowledged by the slave - something went wrong.");
mCmd.cmd = SHIP_NONE;
return;
}
// now wait for slave response (will be received by react method)
sc_core::wait(mResponseEvent);
MasterDataType data;
data.set(th->get_mData());
sc_uint<64> *objdata = new sc_uint<64>[(int)(data.byte_size()/8)];
for (unsigned int i=0, j=0; i<data.byte_size(); i+=8, j++) {
objdata[j](63,56) = data[i];
objdata[j](55,48) = data[i+1];
objdata[j](47,40) = data[i+2];
objdata[j](39,32) = data[i+3];
objdata[j](31,24) = data[i+4];
objdata[j](23,16) = data[i+5];
objdata[j](15,8) = data[i+6];
objdata[j](7,0) = data[i+7];
}
obj.deserialize(objdata, data.byte_size()/8);
delete[] objdata;
// we are done -> send ResponseACK
// TODO: remove SC_ZERO_TIME parameter (move it into generic.h)
PORT::AckResponse(ah, phase, SC_ZERO_TIME);
GS_TRACE(name(), "request() received slave response OK.");
}
mCmd.cmd = SHIP_NONE;
}
/// Wrapper for bind operator
void operator() (tlm_port<b_if_type,if_type>& other) {
PORT::operator()(other);
}
/// Wrapper for bind operator
void operator() (tlm_multi_port<b_if_type,if_type>& other) {
PORT::operator()(other);
}
void operator() (tlm_port_forwarder_base<b_if_type,if_type>& other) {
PORT::operator()(other);
}
protected:
/**
* Play the PVT protocol with the slave
*/
void react() {
accessHandle ah = PORT::get_transactionHandle();
phaseHandle ph = PORT::get_phase();
PVTProcess(ah, ph);
}
void PVTProcess(accessHandle ah, phaseHandle ph) {
switch (ph.state) {
case GenericPhase::ResponseValid: // slave sends response
{
mCmd.burstlength = ah->get_mBurstLength()/8;
GS_TRACE(name(), "react() received slave response (a SHIP object).");
// notify request() method
mResponseEvent.notify();
}
break;
default:
break; // do nothing
}
}
protected:
/// Address range of this SHIP port
MAddr mAddr;
/// Use PVT transfer mode?
bool mPVT;
/// The current SHIP command
ship_command mCmd;
/// Internal notification
sc_event mResponseEvent;
};
//---------------------------------------------------------------------------
/**
* SHIP slave API implementation based on the GreenBus TLM fabric.
*/
//---------------------------------------------------------------------------
template <class T>
class shipSlaveAPI
: public GenericTargetPort,
public tlm_b_if<GenericTransaction_P>,
public tlm_slave_if<MAddr>
{
public:
typedef GenericTargetPort PORT;
typedef typename PORT::transaction transaction;
typedef typename PORT::transactionHandle transactionHandle;
typedef typename PORT::accessHandle accessHandle;
typedef typename PORT::phaseHandle phaseHandle;
SC_HAS_PROCESS(shipSlaveAPI);
shipSlaveAPI ( sc_module_name port_name, bool pvtmode = false ) :
PORT(port_name),
mBaseAddr(0x0),
mHighAddr(0x0),
mPVT(pvtmode),
mReplyObj(NULL)
{
// DUST structure analysis
#ifdef DUST_ENABLE
DUST_SLAVE_PORT("ShipSlaveAPI", "SHIP");
#endif
PORT::bind_b_if(*this);
SC_METHOD(react); // this method handles the PVT protocol
sensitive << PORT::default_event();
dont_initialize();
mCmd.cmd = SHIP_NONE;
mCmd.burstlength = 0;
}
shipSlaveAPI ( sc_module_name port_name, MAddr base_addr, MAddr high_addr, bool pvtmode = false ) :
PORT(port_name),
mBaseAddr(base_addr),
mHighAddr(high_addr),
mPVT(pvtmode),
mReplyObj(NULL)
{
// DUST structure analysis
#ifdef DUST_ENABLE
DUST_SLAVE_PORT("ShipSlaveAPI", "SHIP");
#endif
PORT::bind_b_if(*this);
SC_METHOD(react); // this method handles the PVT protocol
sensitive << PORT::default_event();
dont_initialize();
mCmd.cmd = SHIP_NONE;
mCmd.burstlength = 0;
}
/// Receive a SHIP object from a master
/**
* The recv method copies the last received
* SHIP object into a user SHIP object.
* @param obj A reference to a SHIP object. The received
* object will be copied into this object.
*/
void recv(T& obj) {
if (mCmd.cmd != SHIP_SEND) {
SC_REPORT_ERROR(name(), "recv() called, but not in RECV state");
return;
}
if (!mPVT) { // PV mode
// we received a pointer to the master's SHIP object,
// so do a direct copy
obj = *mObj;
mTransactEvent.notify(); // notify b_transact
}
else { // PVT mode
accessHandle ah = PORT::get_transactionHandle();
phaseHandle ph = PORT::get_phase();
// we received an array of PVT data quarks,
// so deserialize them to the slave's SHIP object
MasterDataType data;
data.set(ah->get_mData());
sc_uint<64> *objdata = new sc_uint<64>[(int)(data.byte_size()/8)];
for (unsigned int i=0, j=0; i<data.byte_size(); i+=8, j++) {
objdata[j](63,56) = data[i];
objdata[j](55,48) = data[i+1];
objdata[j](47,40) = data[i+2];
objdata[j](39,32) = data[i+3];
objdata[j](31,24) = data[i+4];
objdata[j](23,16) = data[i+5];
objdata[j](15,8) = data[i+6];
objdata[j](7,0) = data[i+7];
}
obj.deserialize(objdata, data.byte_size()/8);
delete[] objdata;
// send DataACK
// TODO: remove SC_ZERO_TIME parameter (move it into generic.h)
PORT::AckData(ah, ph, SC_ZERO_TIME);
}
mCmd.cmd = SHIP_NONE; // idle again
GS_TRACE(name(), "recv() delivered a SHIP object.");
}
/// Send a SHIP object to a master in answer to a request
/**
* The reply method sends a SHIP object to a master
* in answer to a SHIP request.
* @param obj The object to send to the master.
*/
void reply(T& obj) {
mCmd.burstlength = obj.getSerialLength();
if (!mPVT) { // PV bypass mode
sc_assert(mRequestHandle != NULL);
// create local copy of reply data
if (mReplyObj != NULL)
delete mReplyObj;
mReplyObj = new T();
*mReplyObj = obj;
mRequestHandle->set_mBurstLength(sizeof(T*)); // only one complex data object
mRequestHandle->set_mData(MasterDataType((unsigned char*)mReplyObj, sizeof(T*)));
GS_TRACE(name(), "reply() copied SHIP object to transaction container.");
mCmd.cmd = SHIP_NONE;
mTransactEvent.notify(); // notify b_transact
}
else { // PVT transaction
accessHandle ah = PORT::get_transactionHandle();
transactionHandle th = boost::dynamic_pointer_cast<transaction>(ah);
phaseHandle phase;
unsigned int objbytes = obj.getSerialLength()*8;
sc_uint<64> *objdata = obj.serialize();
th->set_mBurstLength(objbytes); // serial length is 64 bit aligned
unsigned char *data = new unsigned char[objbytes];
// copy serialized SHIP object to a temporal char array
for (unsigned int i=0, j=0; i<objbytes; i+=8, j++) {
data[i] = objdata[j](63,56);
data[i+1] = objdata[j](55,48);
data[i+2] = objdata[j](47,40);
data[i+3] = objdata[j](39,32);
data[i+4] = objdata[j](31,24);
data[i+5] = objdata[j](23,16);
data[i+6] = objdata[j](15,8);
data[i+7] = objdata[j](7,0);
}
th->set_mData(MasterDataType(data, objbytes));
// PVT response phase
GS_TRACE(name(), "reply() starts response phase.");
PORT::Response.block(th);
delete data;
phase = PORT::get_phase();
if (phase.state == GenericPhase::ResponseAccepted) {
GS_TRACE(name(), "reply() response has been accepted by the master.");
} else {
SC_REPORT_ERROR(name(), "reply() response was not acknowledged by the master - something went wrong.");
}
mCmd.cmd = SHIP_NONE;
}
}
/// Wait for commands from a master
/**
* For each SHIP slave port, a SC_THREAD has to be implemented
* which handles commands from the SHIP master connected
* to this slave port. This can be performed by calling the
* waitEvent method, which does not return unless a SHIP command
* has been received.
*
* A typical implementation would look like this:
* <pre>
* ship_command comm;
* while(1) {
* comm = slave_port->waitEvent();
* switch(comm.cmd) {
* case SHIP_SEND:
* // receive data using slave_port->recv(...)
* break;
* case SHIP_REQUEST:
* // send data using slave_port->reply(...)
* break;
* default:
* cout << "Unknown ship_command: " << comm.cmd << endl;
* }
* }
* </pre>
*
* @return The return value is a ship_command-structure.
* @see ship_command
*/
ship_command waitEvent() {
if (mCmd.cmd != SHIP_NONE) // return immediately if we are inside a transaction
return mCmd;
else {
do { // wait for something to happen
sc_core::wait(mEvent);
} while(mCmd.cmd == SHIP_NONE);
return mCmd;
}
}
/**
* Play the PVT protocol with the master.
*/
void react() {
accessHandle ah = PORT::get_transactionHandle();
phaseHandle ph = PORT::get_phase();
PVTProcess(ah, ph);
}
void PVTProcess(accessHandle ah, phaseHandle ph) {
switch (ph.state) {
case GenericPhase::RequestValid: // master sends request
{
if (mCmd.cmd != SHIP_NONE) {
SC_REPORT_ERROR(name(), "react() got request, but is already handling another transaction. Split transactions are not allowed with SHIP.");
}
else {
// TODO: remove SC_ZERO_TIME parameter (move it into generic.h)
PORT::AckRequest(ah, ph, SC_ZERO_TIME); // immediate ACK
if (ah->get_mCmd() == Generic_MCMD_WR) {
GS_TRACE(name(), "react() accepted SEND request. Now waiting for data.");
mCmd.cmd = SHIP_SEND;
}
else if (ah->get_mCmd() == Generic_MCMD_RD) {
GS_TRACE(name(), "react() accepted REQUEST request. Now waiting for slave to call reply().");
mCmd.cmd = SHIP_REQUEST;
mEvent.notify();
}
else {
SC_REPORT_ERROR(name(), "react() got unknown request. Ignoring.");
}
}
}
break;
case GenericPhase::DataAccepted: // master acknowledges data (should this happen??)
{
GS_TRACE(name(), "react() got dataAccepted. This should not happen!? (slave uses response phase!)");
}
break;
case GenericPhase::DataValid: // master sends data
{
mCmd.burstlength = ah->get_mBurstLength()/8;
GS_TRACE(name(), "react() received a SHIP object. Now waiting for slave to call recv().");
// notify waitEvent() method
mEvent.notify();
}
break;
case GenericPhase::ResponseAccepted: // master acknowledges response
{
GS_TRACE(name(), "react() got responseAccepted. Transaction finished OK.");
}
break;
default:
{
SC_REPORT_ERROR(name(), "react() got triggered with unexpected phase.");
}
}
}
/**
* The tlm_b_if PV transaction implementation
*/
virtual void b_transact(transactionHandle th) {
sc_assert(mCmd.cmd == SHIP_NONE);
if (th->get_mCmd() == Generic_MCMD_WR) { // master sends data
MasterDataType data;
data.set(th->get_mData());
mObj = ((T*)&data[0]);
mCmd.cmd = SHIP_SEND;
mCmd.burstlength = mObj->getSerialLength();
GS_TRACE(name(), "b_transact() PV-received a SHIP object. Now waiting for slave to call recv().");
// notify waitEvent method
mEvent.notify();
// wait for finalization of this request by the slave
sc_core::wait(mTransactEvent);
}
else if (th->get_mCmd() == Generic_MCMD_RD) { // master requests data
mCmd.cmd = SHIP_REQUEST;
mCmd.burstlength = th->get_mBurstLength();
// save request handle in class member
mRequestHandle = th;
GS_TRACE(name(), "b_transact() PV-received a SHIP request. Now waiting for slave to call request().");
// notify waitEvent method
mEvent.notify();
// wait for finalization of this request by the slave
sc_core::wait(mTransactEvent);
}
}
/**
* The tlm_slave_if set address function.
*/
virtual void setAddress(MAddr base, MAddr high) {
mBaseAddr = base;
mHighAddr = high;
}
/**
* The tlm_slave_if get address function.
*/
virtual void getAddress(MAddr& base, MAddr& high) {
base = mBaseAddr;
high = mHighAddr;
}
void operator() (tlm_port_forwarder_base<b_if_type,if_type>& other) {
PORT::operator()(other);
}
protected:
/// Address range of this SHIP port
MAddr mBaseAddr, mHighAddr;
/// Use PVT transfer mode?
bool mPVT;
/// The current ship object
T *mObj, *mReplyObj;
/// State change event
sc_event mEvent, mTransactEvent;
/// The current state
ship_command mCmd;
/// Handle to the current PV request
transactionHandle mRequestHandle;
};
template <class T>
class shipMasterAPIForward: public basic_port_forwarder<shipMasterAPI<T>, shipSlaveAPI<T>, GenericTransaction, GenericPhase> {
public:
shipMasterAPIForward(sc_module_name name): basic_port_forwarder<shipMasterAPI<T>, shipSlaveAPI<T>, GenericTransaction, GenericPhase>(name){}
};
template <class T>
class shipSlaveAPIForward: public basic_port_forwarder<shipSlaveAPI<T>, shipMasterAPI<T>, GenericTransaction, GenericPhase> {
public:
shipSlaveAPIForward(sc_module_name name): basic_port_forwarder<shipSlaveAPI<T>, shipMasterAPI<T>, GenericTransaction, GenericPhase>(name){}
};
#endif
See more files for this project here