| /***************************************************************************** | |
| Licensed to Accellera Systems Initiative Inc. (Accellera) under one or | |
| more contributor license agreements. See the NOTICE file distributed | |
| with this work for additional information regarding copyright ownership. | |
| Accellera licenses this file to you under the Apache License, Version 2.0 | |
| (the "License"); you may not use this file except in compliance with the | |
| License. You may obtain a copy of the License at | |
| http://www.apache.org/licenses/LICENSE-2.0 | |
| Unless required by applicable law or agreed to in writing, software | |
| distributed under the License is distributed on an "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| implied. See the License for the specific language governing | |
| permissions and limitations under the License. | |
| *****************************************************************************/ | |
| //==================================================================== | |
| // Nov 06, 2008 | |
| // | |
| // Updated by: | |
| // Xiaopeng Qiu, JEDA Technologies, Inc | |
| // Email: qiuxp@jedatechnologies.net | |
| // | |
| // To fix violations of TLM2.0 rules, which are detected by JEDA | |
| // TLM2.0 checker. | |
| // | |
| //==================================================================== | |
| #ifndef __SIMPLE_AT_INITIATOR1_H__ | |
| #define __SIMPLE_AT_INITIATOR1_H__ | |
| #include "tlm.h" | |
| #include "tlm_utils/simple_initiator_socket.h" | |
| //#include <systemc> | |
| #include <cassert> | |
| #include <queue> | |
| //#include <iostream> | |
| class SimpleATInitiator1 : public sc_core::sc_module | |
| { | |
| public: | |
| typedef tlm::tlm_generic_payload transaction_type; | |
| typedef tlm::tlm_phase phase_type; | |
| typedef tlm::tlm_sync_enum sync_enum_type; | |
| typedef tlm_utils::simple_initiator_socket<SimpleATInitiator1> initiator_socket_type; | |
| public: | |
| // extended transaction, holds tlm_generic_payload + data storage | |
| template <typename DT> | |
| class MyTransaction : public transaction_type | |
| { | |
| public: | |
| MyTransaction() | |
| { | |
| this->set_data_ptr(reinterpret_cast<unsigned char*>(&mData)); | |
| } | |
| MyTransaction(tlm::tlm_mm_interface* mm) : transaction_type(mm) | |
| { | |
| this->set_data_ptr(reinterpret_cast<unsigned char*>(&mData)); | |
| } | |
| void setData(DT& data) { mData = data; } | |
| DT getData() const { return mData; } | |
| private: | |
| DT mData; | |
| }; | |
| typedef MyTransaction<unsigned int> mytransaction_type; | |
| // Dummy Transaction Pool | |
| class SimplePool : public tlm::tlm_mm_interface | |
| { | |
| public: | |
| SimplePool() {} | |
| mytransaction_type* claim() | |
| { | |
| mytransaction_type* t = new mytransaction_type(this); | |
| t->acquire(); | |
| return t; | |
| } | |
| void release(mytransaction_type* t) | |
| { | |
| t->release(); | |
| } | |
| void free(tlm::tlm_generic_payload* t) | |
| { | |
| t->reset(); | |
| delete t; | |
| } | |
| }; | |
| public: | |
| initiator_socket_type socket; | |
| public: | |
| SC_HAS_PROCESS(SimpleATInitiator1); | |
| SimpleATInitiator1(sc_core::sc_module_name name, | |
| unsigned int nrOfTransactions = 0x5, | |
| unsigned int baseAddress = 0x0) : | |
| sc_core::sc_module(name), | |
| socket("socket"), | |
| ACCEPT_DELAY(10, sc_core::SC_NS), | |
| mNrOfTransactions(nrOfTransactions), | |
| mBaseAddress(baseAddress), | |
| mTransactionCount(0), | |
| mCurrentTransaction(0) | |
| { | |
| // register nb_transport method | |
| socket.register_nb_transport_bw(this, &SimpleATInitiator1::myNBTransport); | |
| // Initiator thread | |
| SC_THREAD(run); | |
| SC_METHOD(endResponse) | |
| sensitive << mEndResponseEvent; | |
| dont_initialize(); | |
| } | |
| bool initTransaction(mytransaction_type*& trans) | |
| { | |
| if (mTransactionCount < mNrOfTransactions) { | |
| trans = transPool.claim(); | |
| trans->set_address(mBaseAddress + 4*mTransactionCount); | |
| trans->setData(mTransactionCount); | |
| trans->set_command(tlm::TLM_WRITE_COMMAND); | |
| } else if (mTransactionCount < 2 * mNrOfTransactions) { | |
| trans = transPool.claim(); | |
| trans->set_address(mBaseAddress + 4*(mTransactionCount - mNrOfTransactions)); | |
| trans->set_command(tlm::TLM_READ_COMMAND); | |
| } else { | |
| return false; | |
| } | |
| trans->set_data_length(4); | |
| trans->set_streaming_width(4); | |
| ++mTransactionCount; | |
| return true; | |
| } | |
| void logStartTransation(mytransaction_type& trans) | |
| { | |
| if (trans.get_command() == tlm::TLM_WRITE_COMMAND) { | |
| std::cout << name() << ": Send write request: A = 0x" | |
| << std::hex << (unsigned int)trans.get_address() | |
| << ", D = 0x" << trans.getData() << std::dec | |
| << " @ " << sc_core::sc_time_stamp() << std::endl; | |
| } else { | |
| std::cout << name() << ": Send read request: A = 0x" | |
| << std::hex << (int)trans.get_address() << std::dec | |
| << " @ " << sc_core::sc_time_stamp() << std::endl; | |
| } | |
| } | |
| void logEndTransaction(mytransaction_type& trans) | |
| { | |
| if (trans.get_response_status() != tlm::TLM_OK_RESPONSE) { | |
| std::cout << name() << ": Received error response @ " | |
| << sc_core::sc_time_stamp() << std::endl; | |
| } else { | |
| std::cout << name() << ": Received ok response"; | |
| if (trans.get_command() == tlm::TLM_READ_COMMAND) { | |
| std::cout << ": D = 0x" << trans.getData() << std::dec; | |
| } | |
| std::cout << " @ " << sc_core::sc_time_stamp() << std::endl; | |
| } | |
| } | |
| // | |
| // Simple AT Initiator | |
| // - Request must be accepted by the target before the next request can be | |
| // send | |
| // - Responses can come out of order | |
| // - Responses will be accepted after fixed delay | |
| // | |
| void run() | |
| { | |
| phase_type phase; | |
| sc_core::sc_time t; | |
| mytransaction_type* ptrans; | |
| while (initTransaction(ptrans)) { | |
| // Create transaction and initialise phase and t | |
| mytransaction_type& trans = *ptrans; | |
| phase = tlm::BEGIN_REQ; | |
| t = sc_core::SC_ZERO_TIME; | |
| logStartTransation(trans); | |
| switch (socket->nb_transport_fw(trans, phase, t)) { | |
| case tlm::TLM_COMPLETED: | |
| // Transaction Finished, wait for the returned delay | |
| wait(t); | |
| logEndTransaction(trans); | |
| transPool.release(&trans); | |
| break; | |
| case tlm::TLM_ACCEPTED: | |
| case tlm::TLM_UPDATED: | |
| switch (phase) { | |
| case tlm::BEGIN_REQ: | |
| // Request phase not yet finished | |
| // Wait until end of request phase before sending new request | |
| // FIXME | |
| mCurrentTransaction = &trans; | |
| wait(mEndRequestPhase); | |
| mCurrentTransaction = 0; | |
| break; | |
| case tlm::END_REQ: | |
| // Request phase ended | |
| if (t != sc_core::SC_ZERO_TIME) { | |
| // Wait until end of request time before sending new request | |
| wait(t); | |
| } | |
| break; | |
| case tlm::BEGIN_RESP: | |
| // Request phase ended and response phase already started | |
| if (t != sc_core::SC_ZERO_TIME) { | |
| // Wait until end of request time before sending new request | |
| wait(t); | |
| } | |
| if (mEndResponseQueue.empty()) { | |
| // Notify end of response phase after ACCEPT delay | |
| mEndResponseEvent.notify(ACCEPT_DELAY); | |
| } | |
| mEndResponseQueue.push(&trans); | |
| break; | |
| case tlm::END_RESP: // fall-through | |
| default: | |
| // A target should never return with these phases | |
| // If phase == END_RESP, nb_transport should have returned true | |
| assert(0); exit(1); | |
| break; | |
| } | |
| break; | |
| default: | |
| assert(0); exit(1); | |
| }; | |
| } | |
| wait(); | |
| } | |
| sync_enum_type myNBTransport(transaction_type& trans, phase_type& phase, sc_core::sc_time& t) | |
| { | |
| switch (phase) { | |
| case tlm::END_REQ: | |
| assert(t == sc_core::SC_ZERO_TIME); // FIXME: can t != 0? | |
| // Request phase ended | |
| mEndRequestPhase.notify(sc_core::SC_ZERO_TIME); | |
| return tlm::TLM_ACCEPTED; | |
| case tlm::BEGIN_RESP: | |
| { | |
| assert(t == sc_core::SC_ZERO_TIME); // FIXME: can t != 0? | |
| // Notify end of request phase if run thread is waiting for it | |
| // FIXME | |
| if (&trans == mCurrentTransaction) { | |
| mEndRequestPhase.notify(sc_core::SC_ZERO_TIME); | |
| } | |
| assert(dynamic_cast<mytransaction_type*>(&trans)); | |
| mytransaction_type* myTrans = static_cast<mytransaction_type*>(&trans); | |
| assert(myTrans); | |
| if (mEndResponseQueue.empty()) { | |
| // Notify end of response phase after ACCEPT delay | |
| mEndResponseEvent.notify(ACCEPT_DELAY); | |
| } | |
| mEndResponseQueue.push(myTrans); | |
| return tlm::TLM_ACCEPTED; | |
| } | |
| case tlm::BEGIN_REQ: // fall-through | |
| case tlm::END_RESP: // fall-through | |
| default: | |
| // A target should never call nb_transport with these phases | |
| assert(0); exit(1); | |
| // return tlm::TLM_COMPLETED; //unreachable code | |
| }; | |
| } | |
| void endResponse() | |
| { | |
| assert(!mEndResponseQueue.empty()); | |
| // end response phase | |
| phase_type phase = tlm::END_RESP; | |
| sc_core::sc_time t = sc_core::SC_ZERO_TIME; | |
| mytransaction_type* trans = mEndResponseQueue.front(); | |
| assert(trans); | |
| mEndResponseQueue.pop(); | |
| #if ( ! NDEBUG ) | |
| sync_enum_type r = socket->nb_transport_fw(*trans, phase, t); | |
| #endif /* ! NDEBUG */ | |
| assert(r == tlm::TLM_COMPLETED); // FIXME: target should return TLM_COMPLETED? | |
| assert(t == sc_core::SC_ZERO_TIME); // t must be SC_ZERO_TIME | |
| logEndTransaction(*trans); | |
| transPool.release(trans); | |
| if (!mEndResponseQueue.empty()) { | |
| // Notify end of response phase after ACCEPT delay | |
| mEndResponseEvent.notify(ACCEPT_DELAY); | |
| } | |
| } | |
| private: | |
| const sc_core::sc_time ACCEPT_DELAY; | |
| private: | |
| unsigned int mNrOfTransactions; | |
| unsigned int mBaseAddress; | |
| SimplePool transPool; | |
| unsigned int mTransactionCount; | |
| sc_core::sc_event mEndRequestPhase; | |
| std::queue<mytransaction_type*> mEndResponseQueue; | |
| sc_core::sc_event mEndResponseEvent; | |
| transaction_type* mCurrentTransaction; | |
| }; | |
| #endif |