diff options
author | Gabe Black <gabeblack@google.com> | 2018-06-15 20:02:54 -0700 |
---|---|---|
committer | Gabe Black <gabeblack@google.com> | 2018-08-28 21:16:31 +0000 |
commit | eba0ab506bfa33e7810163bc255d0af4a14ac8db (patch) | |
tree | c269137367fb7a66d1377909be06f6c883f368d9 /src/systemc/tests/include/SimpleBusAT.h | |
parent | dc0b98dcdcc8f3665eac4d924060a51493862f2e (diff) | |
download | gem5-eba0ab506bfa33e7810163bc255d0af4a14ac8db.tar.xz |
systemc: Add some common test include files.
These are "common" in the sense that they're not in a particular test
directory, but I think they're only used by one test.
Change-Id: I4ffd209d04ed0e5253085810913827b87412b302
Reviewed-on: https://gem5-review.googlesource.com/11272
Reviewed-by: Gabe Black <gabeblack@google.com>
Maintainer: Gabe Black <gabeblack@google.com>
Diffstat (limited to 'src/systemc/tests/include/SimpleBusAT.h')
-rw-r--r-- | src/systemc/tests/include/SimpleBusAT.h | 378 |
1 files changed, 378 insertions, 0 deletions
diff --git a/src/systemc/tests/include/SimpleBusAT.h b/src/systemc/tests/include/SimpleBusAT.h new file mode 100644 index 000000000..db680338c --- /dev/null +++ b/src/systemc/tests/include/SimpleBusAT.h @@ -0,0 +1,378 @@ +/***************************************************************************** + + 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. + + *****************************************************************************/ + +#ifndef __SIMPLEBUSAT_H__ +#define __SIMPLEBUSAT_H__ + +//#include <systemc> +#include "tlm.h" + +#include "tlm_utils/simple_target_socket.h" +#include "tlm_utils/simple_initiator_socket.h" + +#include "tlm_utils/peq_with_get.h" + +template <int NR_OF_INITIATORS, int NR_OF_TARGETS> +class SimpleBusAT : 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_target_socket_tagged<SimpleBusAT> target_socket_type; + typedef tlm_utils::simple_initiator_socket_tagged<SimpleBusAT> initiator_socket_type; + +public: + target_socket_type target_socket[NR_OF_INITIATORS]; + initiator_socket_type initiator_socket[NR_OF_TARGETS]; + +public: + SC_HAS_PROCESS(SimpleBusAT); + SimpleBusAT(sc_core::sc_module_name name) : + sc_core::sc_module(name), + mRequestPEQ("requestPEQ"), + mResponsePEQ("responsePEQ") + { + for (unsigned int i = 0; i < NR_OF_INITIATORS; ++i) { + target_socket[i].register_nb_transport_fw(this, &SimpleBusAT::initiatorNBTransport, i); + target_socket[i].register_transport_dbg(this, &SimpleBusAT::transportDebug, i); + target_socket[i].register_get_direct_mem_ptr(this, &SimpleBusAT::getDMIPointer, i); + } + for (unsigned int i = 0; i < NR_OF_TARGETS; ++i) { + initiator_socket[i].register_nb_transport_bw(this, &SimpleBusAT::targetNBTransport, i); + initiator_socket[i].register_invalidate_direct_mem_ptr(this, &SimpleBusAT::invalidateDMIPointers, i); + } + + SC_THREAD(RequestThread); + SC_THREAD(ResponseThread); + } + + // + // Dummy decoder: + // - address[31-28]: portId + // - address[27-0]: masked address + // + + unsigned int getPortId(const sc_dt::uint64& address) + { + return (unsigned int)address >> 28; + } + + sc_dt::uint64 getAddressOffset(unsigned int portId) + { + return portId << 28; + } + + sc_dt::uint64 getAddressMask(unsigned int portId) + { + return 0xfffffff; + } + + unsigned int decode(const sc_dt::uint64& address) + { + // decode address: + // - return initiator socket id + + return getPortId(address); + } + + // + // AT protocol + // + + void RequestThread() + { + while (true) { + wait(mRequestPEQ.get_event()); + + transaction_type* trans; + while ((trans = mRequestPEQ.get_next_transaction())!=0) { + unsigned int portId = decode(trans->get_address()); + assert(portId < NR_OF_TARGETS); + initiator_socket_type* decodeSocket = &initiator_socket[portId]; + trans->set_address(trans->get_address() & getAddressMask(portId)); + + // Fill in the destination port + PendingTransactionsIterator it = mPendingTransactions.find(trans); + assert(it != mPendingTransactions.end()); + it->second.to = decodeSocket; + + phase_type phase = tlm::BEGIN_REQ; + sc_core::sc_time t = sc_core::SC_ZERO_TIME; + + // FIXME: No limitation on number of pending transactions + // All targets (that return false) must support multiple transactions + switch ((*decodeSocket)->nb_transport_fw(*trans, phase, t)) { + case tlm::TLM_ACCEPTED: + case tlm::TLM_UPDATED: + // Transaction not yet finished + if (phase == tlm::BEGIN_REQ) { + // Request phase not yet finished + wait(mEndRequestEvent); + + } else if (phase == tlm::END_REQ) { + // Request phase finished, but response phase not yet started + wait(t); + + } else if (phase == tlm::BEGIN_RESP) { + mResponsePEQ.notify(*trans, t); + // Not needed to send END_REQ to initiator + continue; + + } else { // END_RESP + assert(0); exit(1); + } + + // only send END_REQ to initiator if BEGIN_RESP was not already send + if (it->second.from) { + phase = tlm::END_REQ; + t = sc_core::SC_ZERO_TIME; + (*it->second.from)->nb_transport_bw(*trans, phase, t); + } + + break; + + case tlm::TLM_COMPLETED: + // Transaction finished + mResponsePEQ.notify(*trans, t); + + // reset to destination port (we must not send END_RESP to target) + it->second.to = 0; + + wait(t); + break; + + default: + assert(0); exit(1); + }; + } + } + } + + void ResponseThread() + { + while (true) { + wait(mResponsePEQ.get_event()); + + transaction_type* trans; + while ((trans = mResponsePEQ.get_next_transaction())!=0) { + PendingTransactionsIterator it = mPendingTransactions.find(trans); + assert(it != mPendingTransactions.end()); + + phase_type phase = tlm::BEGIN_RESP; + sc_core::sc_time t = sc_core::SC_ZERO_TIME; + + target_socket_type* initiatorSocket = it->second.from; + // if BEGIN_RESP is send first we don't have to send END_REQ anymore + it->second.from = 0; + + switch ((*initiatorSocket)->nb_transport_bw(*trans, phase, t)) { + case tlm::TLM_COMPLETED: + // Transaction finished + wait(t); + break; + + case tlm::TLM_ACCEPTED: + case tlm::TLM_UPDATED: + // Transaction not yet finished + wait(mEndResponseEvent); + break; + + default: + assert(0); exit(1); + }; + + // forward END_RESP to target + if (it->second.to) { + phase = tlm::END_RESP; + t = sc_core::SC_ZERO_TIME; + #if ( ! NDEBUG ) + sync_enum_type r = (*it->second.to)->nb_transport_fw(*trans, phase, t); + #endif /* ! NDEBUG */ + assert(r == tlm::TLM_COMPLETED); + } + + mPendingTransactions.erase(it); + trans->release(); + } + } + } + + // + // interface methods + // + + sync_enum_type initiatorNBTransport(int initiator_id, + transaction_type& trans, + phase_type& phase, + sc_core::sc_time& t) + { + if (phase == tlm::BEGIN_REQ) { + trans.acquire(); + addPendingTransaction(trans, 0, initiator_id); + + mRequestPEQ.notify(trans, t); + + } else if (phase == tlm::END_RESP) { + mEndResponseEvent.notify(t); + return tlm::TLM_COMPLETED; + + } else { + std::cout << "ERROR: '" << name() + << "': Illegal phase received from initiator." << std::endl; + assert(false); exit(1); + } + + return tlm::TLM_ACCEPTED; + } + + sync_enum_type targetNBTransport(int portId, + transaction_type& trans, + phase_type& phase, + sc_core::sc_time& t) + { + if (phase != tlm::END_REQ && phase != tlm::BEGIN_RESP) { + std::cout << "ERROR: '" << name() + << "': Illegal phase received from target." << std::endl; + assert(false); exit(1); + } + + mEndRequestEvent.notify(t); + if (phase == tlm::BEGIN_RESP) { + mResponsePEQ.notify(trans, t); + } + + return tlm::TLM_ACCEPTED; + } + + unsigned int transportDebug(int initiator_id, transaction_type& trans) + { + unsigned int portId = decode(trans.get_address()); + assert(portId < NR_OF_TARGETS); + initiator_socket_type* decodeSocket = &initiator_socket[portId]; + trans.set_address( trans.get_address() & getAddressMask(portId) ); + + return (*decodeSocket)->transport_dbg(trans); + } + + bool limitRange(unsigned int portId, sc_dt::uint64& low, sc_dt::uint64& high) + { + sc_dt::uint64 addressOffset = getAddressOffset(portId); + sc_dt::uint64 addressMask = getAddressMask(portId); + + if (low > addressMask) { + // Range does not overlap with addressrange for this target + return false; + } + + low += addressOffset; + if (high > addressMask) { + high = addressOffset + addressMask; + + } else { + high += addressOffset; + } + return true; + } + + bool getDMIPointer(int initiator_id, + transaction_type& trans, + tlm::tlm_dmi& dmi_data) + { + // FIXME: DMI not supported for AT bus? + sc_dt::uint64 address = trans.get_address(); + + unsigned int portId = decode(address); + assert(portId < NR_OF_TARGETS); + initiator_socket_type* decodeSocket = &initiator_socket[portId]; + sc_dt::uint64 maskedAddress = address & getAddressMask(portId); + + trans.set_address(maskedAddress); + + bool result = + (*decodeSocket)->get_direct_mem_ptr(trans, dmi_data); + + if (result) + { + // Range must contain address + assert(dmi_data.get_start_address() <= maskedAddress); + assert(dmi_data.get_end_address() >= maskedAddress); + } + + // Should always succeed + sc_dt::uint64 start, end; + start = dmi_data.get_start_address(); + end = dmi_data.get_end_address(); + + limitRange(portId, start, end); + + dmi_data.set_start_address(start); + dmi_data.set_end_address(end); + + return result; + } + + void invalidateDMIPointers(int portId, + sc_dt::uint64 start_range, + sc_dt::uint64 end_range) + { + // FIXME: probably faster to always invalidate everything? + + if ((portId >= 0) && !limitRange(portId, start_range, end_range)) { + // Range does not fall into address range of target + return; + } + + for (unsigned int i = 0; i < NR_OF_INITIATORS; ++i) { + (target_socket[i])->invalidate_direct_mem_ptr(start_range, end_range); + } + } + +private: + void addPendingTransaction(transaction_type& trans, + initiator_socket_type* to, + int initiatorId) + { + const ConnectionInfo info = { &target_socket[initiatorId], to }; + assert(mPendingTransactions.find(&trans) == mPendingTransactions.end()); + mPendingTransactions[&trans] = info; + } + +private: + struct ConnectionInfo { + target_socket_type* from; + initiator_socket_type* to; + }; + typedef std::map<transaction_type*, ConnectionInfo> PendingTransactions; + typedef typename PendingTransactions::iterator PendingTransactionsIterator; + typedef typename PendingTransactions::const_iterator PendingTransactionsConstIterator; + +private: + PendingTransactions mPendingTransactions; + + tlm_utils::peq_with_get<transaction_type> mRequestPEQ; + sc_core::sc_event mBeginRequestEvent; + sc_core::sc_event mEndRequestEvent; + + tlm_utils::peq_with_get<transaction_type> mResponsePEQ; + sc_core::sc_event mBeginResponseEvent; + sc_core::sc_event mEndResponseEvent; +}; + +#endif |