Show ship_slaveport.h syntax highlighted
#ifndef __ship_slaveport_h__
#define __ship_slaveport_h__
#include "ship_serializable_if.h"
#include "ship_datatypes.h"
namespace tlm {
//---------------------------------------------------------------------------
/**
* This is the SHIP slave port.
* Use base_addr and high_addr parameter to configure the address range if
* connected to a router.
* Use mode parameter to configure operation mode of this port (default is SHIP_MODE_PV).
*/
//---------------------------------------------------------------------------
template <class T>
class shipSlaveAPI
: public GenericSlavePort,
public tlm_b_if<GenericTransaction_P>
{
public:
typedef GenericSlavePort PORT;
typedef typename PORT::transaction transaction;
typedef typename PORT::transactionHandle transactionHandle;
typedef typename PORT::accessHandle accessHandle;
typedef typename PORT::phase phase;
// configurable parameters
gs_param<gs_uint32> mode;
gs_param<gs_uint32> clk_period;
SC_HAS_PROCESS(shipSlaveAPI);
/**
* Create a SHIP slave port. Use configuration framework
* to set address raneg (base_addr, high_addr) and operation mode (mode).
*/
shipSlaveAPI ( sc_module_name port_name ) :
PORT(port_name),
mReplyObj(NULL),
mVector(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();
SC_METHOD(ct_react); // this method handles CT quark update events
sensitive << mCTStartEvent;
dont_initialize();
mCmd.cmd = SHIP_NONE;
mCmd.burstlength = 0;
mVector = new std::vector<gs_uint8>();
GS_PARAM(mode, gs_uint32, SHIP_MODE_PV); // default is PV
GS_PARAM(clk_period, gs_uint32, 10); // default clock period is 10ns (not used in PV mode)
}
/// 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 (mObj != NULL) { // PV mode
// we received a pointer to the master's SHIP object,
// so do a direct copy
obj = *mObj; // mObj has already been set by b_transact
mTransactEvent.notify(); // notify b_transact
}
else { // PVT mode
GSDataType data;
data.set(mah->getMData());
if (data.isPointer()) { // master uses BA performance mode
obj = *(static_cast<T*>(data.getPointer()));
}
else { // received serialized SHIP object
if (mph.getSimulationMode()==MODE_CT) { // CT mode
// notify ct_react method of that there is some work for it now
ct_react_wakeup = true;
mCTStartEvent.notify();
sc_core::wait(mCTFinishEvent); // wait for CT simulation to finish
}
obj.deserialize(data); // deserialize SHIP object from received byte array
}
if (mph.getSimulationMode()!=MODE_CT) {
mah->setSBurstLength(obj.getSerialLength()); // ack complete reception
// acknowledge BA data phase after estimated transfer delay
PORT::AckData(mah, mph, sc_time(clk_period*mah->getMBurstLength()/mah->getSDataWidth(), SC_NS));
// block this method for the time this data transfer would take in a real system
sc_core::wait(sc_time(clk_period*mah->getMBurstLength()/mah->getSDataWidth(), SC_NS));
}
}
mCmd.cmd = SHIP_NONE; // idle again
GS_TRACE(name(), "recv() received a SHIP object.");
}
~shipSlaveAPI() {
if (mVector != NULL)
delete mVector;
}
/**
* Send data in CT simulation mode to the target.
*/
inline void sendResponseCT() {
mCTStartEvent.notify(SC_ZERO_TIME);
PORT::Response.block(mah, mph, SC_ZERO_TIME, MODE_CT); // start CT data phase
}
/// 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) {
sc_assert(mRequestHandle != NULL);
mCmd.burstlength = obj.getSerialLength();
unsigned int objbytes = obj.getSerialLength()*8;
if (mode == SHIP_MODE_PV) { // PV bypass mode
// create local copy of reply data, so that the master can access the data safely until the next request() invocation
if (mReplyObj != NULL)
delete mReplyObj;
mReplyObj = new T(obj);
mRequestHandle->setSBurstLength(objbytes);
GSDataType data;
data.setPointer(mReplyObj);
mRequestHandle->setSData(data);
//GS_TRACE(name(), "reply() set data pointer to local SHIP object in transaction container.");
mCmd.cmd = SHIP_NONE;
mTransactEvent.notify(); // notify b_transact
}
else { // PVT transaction
// map SHIP object onto transaction container
if (mph.getSimulationMode()==MODE_BA && mode==SHIP_MODE_BA_P) { // BA performance mode
mah->getSData().setPointer(static_cast<void*>(&obj));
}
else { // use serialization
mVector->empty();
mah->getSData().setData(*mVector); // TODO: should be mah->setSData(*mVector), but that does a deepcopy, which is not necessary here
obj.serialize(mah->getSData());
}
mah->setSBurstLength(objbytes);
// response phase
if (mph.getSimulationMode()==MODE_CT) { // CT mode
GS_TRACE(name(), "reply() sends response with data size %d, using CT simulation mode.", objbytes);
sendResponseCT();
}
else { // BA mode
GS_TRACE(name(), "reply() sends response with data size %d.", objbytes);
PORT::Response.block(mah, SC_ZERO_TIME);
}
mph==PORT::get_phase();
if (mph.state == GenericPhase::ResponseAccepted) {
GS_TRACE(name(), "reply() response has been accepted by the master.", objbytes);
} else {
char ch[1024];
sprintf(ch, "Slave response was not acknowledged by the master - got phase=%s (%d) - a severe error occured.", mph.toString().c_str(), mph.state);
SC_REPORT_ERROR(name(), ch);
}
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() {
mah = PORT::get_transaction();
mph = PORT::get_phase();
PVTProcess();
}
void PVTProcess() {
switch (mph.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.");
}
else {
PORT::AckRequest(mah, mph, SC_ZERO_TIME, mph.getSimulationMode()); // ack master request and simulation mode
if (mah->getMCmd() == Generic_MCMD_WR) {
mCmd.cmd = SHIP_SEND;
mCmd.burstlength = mah->getMBurstLength();
GS_TRACE(name(), "react() accepted write request, burstlength=%d. Now waiting for master data.", mCmd.burstlength);
mObj = NULL; // indicate PVT mode transaction
}
else if (mah->getMCmd() == Generic_MCMD_RD) {
GS_TRACE(name(), "react() accepted read request. Now waiting for slave to call reply().");
mCmd.cmd = SHIP_REQUEST;
mCmd.burstlength = 0;
mObj = NULL; // indicate PVT mode transaction
mEvent.notify();
}
else {
SC_REPORT_WARNING(name(), "react() got unknown request. Ignoring.");
}
}
}
break;
case GenericPhase::DataAccepted: // master acknowledges data (should this happen??)
{
SC_REPORT_ERROR(name(), "react() got DataAccepted. This should not happen!?");
}
break;
case GenericPhase::DataValid: // master sends data
{
GS_TRACE(name(), "react() got DataValid. 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. SHIP transaction was finished OK.");
}
break;
default:
{
GS_TRACE(name(), "react() got unknown phase [%s]", mph.toString().c_str());
SC_REPORT_WARNING(name(), "react() got triggered with unexpected phase. Ignoring.");
}
}
}
/**
* Play the CT-protocol with the master.
* This method is made dynamically sensitive to the quark update event
* by the recv() and reply() functions when they run in CT simulation mode.
* TODO: here we assume an ideal slave accept delay of 1 cycle per data quark.
* Note that quark size depends on sDataWidth.
*/
void ct_react() {
if (mCmd.cmd == SHIP_SEND) { // we are the receiver
if (ct_react_wakeup) {
ct_react_wakeup=false;
}
else {
mah->notifyInitiatorUpdate(SC_ZERO_TIME); // acknowledge the received quark
mah->setSBurstLength(mah->getSBurstLength()+mah->getSDataWidth()); // increment number of received bytes
//GS_TRACE(name(), "ct_react() received data quark %d", (gs_uint32)mah->getSBurstLength());
if (mah->getSBurstLength()>=mah->getMBurstLength()) { // are we done?
GS_TRACE(name(), "ct_react() received last data quark. Sending AckData.");
PORT::AckData(mah, mph, clk_period, SC_NS, MODE_CT);
mCTFinishEvent.notify();
return;
}
}
// wait for next data quark
next_trigger(mah->getTargetUpdateEvent());
}
else if (mCmd.cmd == SHIP_REQUEST) { // we are the transmitter
// send next data quark
if (mCmd.burstlength>mah->getMBurstLength()) {
mah->notifyInitiatorUpdate(clk_period, SC_NS); // send a data quark with the next rising clock edge
// wait for target acknowledge
next_trigger(mah->getTargetUpdateEvent());
}
else {
GS_TRACE(name(), "ct_react() sent last data quark.");
}
}
}
/**
* The tlm_b_if PV transaction implementation
*/
virtual void b_transact(transactionHandle th) {
sc_assert(mCmd.cmd == SHIP_NONE);
if (th->getMCmd() == Generic_MCMD_WR) { // master sends data
mObj = static_cast<T*>(th->getMData().getPointer());
mCmd.cmd = SHIP_SEND;
mCmd.burstlength = mObj->getSerialLength();
th->setSBurstLength(mCmd.burstlength); // ack burstlength
GS_TRACE(name(), "b_transact() PV-received a SHIP object of size %d. Now waiting for slave to call recv().", (gs_uint32)th->getMBurstLength());
// notify waitEvent method
mEvent.notify();
// wait for finalization of this request by the slave
sc_core::wait(mTransactEvent);
}
else if (th->getMCmd() == Generic_MCMD_RD) { // master requests data
mCmd.cmd = SHIP_REQUEST;
mCmd.burstlength = th->getMBurstLength(); // should be 0
// save transaction container for reply method
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);
}
}
void operator() (tlm_port_forwarder_base<b_if_type,if_type>& other) {
PORT::operator()(other);
}
protected:
/// The current ship object
T *mObj, *mReplyObj;
/// State change event
sc_event mEvent, mTransactEvent;
/// The current state
ship_command mCmd;
/// Handle to the current transaction
accessHandle mRequestHandle;
/// Serialization buffer
std::vector<gs_uint8> *mVector;
/// Internal events for CT simulation
sc_event mCTStartEvent, mCTFinishEvent;
/// Wake-up indicator for ct_react method
bool ct_react_wakeup;
/// The current transcation container
accessHandle mah;
/// The current phase
phase mph;
};
} // namespace tlm
#endif
See more files for this project here