gem5/util/tlm/sc_target.cc

265 lines
8.4 KiB
C++
Raw Normal View History

/*
* Copyright (c) 2015, University of Kaiserslautern
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. Neither the name of the copyright holder 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 HOLDER
* 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.
*
* Authors: Matthias Jung
*/
#include "sc_target.hh"
using namespace sc_core;
using namespace std;
Target::Target(sc_core::sc_module_name name,
bool debug,
unsigned long long int size,
unsigned int offset) :
socket("socket"),
transaction_in_progress(0),
response_in_progress(false),
next_response_pending(0),
end_req_pending(0),
m_peq(this, &Target::peq_cb),
debug(debug),
size(size),
offset(offset)
{
/* Register tlm transport functions */
socket.register_b_transport(this, &Target::b_transport);
socket.register_transport_dbg(this, &Target::transport_dbg);
socket.register_nb_transport_fw(this, &Target::nb_transport_fw);
/* allocate storage memory */
mem = new unsigned char[size];
SC_METHOD(execute_transaction_process);
sensitive << target_done_event;
dont_initialize();
}
void
Target::b_transport(tlm::tlm_generic_payload& trans, sc_time& delay)
{
/* Execute the read or write commands */
execute_transaction(trans);
}
unsigned int
Target::transport_dbg(tlm::tlm_generic_payload& trans)
{
tlm::tlm_command cmd = trans.get_command();
sc_dt::uint64 adr = trans.get_address() - offset;
unsigned char* ptr = trans.get_data_ptr();
unsigned int len = trans.get_data_length();
unsigned char *mem_array_ptr = mem + adr;
/* Load / Store the access: */
if ( cmd == tlm::TLM_READ_COMMAND ) {
if(debug) {
SC_REPORT_INFO("target", "tlm::TLM_READ_COMMAND");
}
std::memcpy(ptr, mem_array_ptr, len);
} else if ( cmd == tlm::TLM_WRITE_COMMAND ) {
if(debug) {
SC_REPORT_INFO("target", "tlm::TLM_WRITE_COMMAND");
}
std::memcpy(mem_array_ptr, ptr, len);
}
return len;
}
/* TLM-2 non-blocking transport method */
tlm::tlm_sync_enum Target::nb_transport_fw(tlm::tlm_generic_payload& trans,
tlm::tlm_phase& phase,
sc_time& delay)
{
/* Queue the transaction until the annotated time has elapsed */
m_peq.notify(trans, phase, delay);
return tlm::TLM_ACCEPTED;
}
void
Target::peq_cb(tlm::tlm_generic_payload& trans,
const tlm::tlm_phase& phase)
{
sc_time delay;
if(phase == tlm::BEGIN_REQ) {
if(debug) SC_REPORT_INFO("target", "tlm::BEGIN_REQ");
/* Increment the transaction reference count */
trans.acquire();
if ( !transaction_in_progress ) {
send_end_req(trans);
} else {
/* Put back-pressure on initiator by deferring END_REQ until
* pipeline is clear */
end_req_pending = &trans;
}
} else if (phase == tlm::END_RESP) {
/* On receiving END_RESP, the target can release the transaction and
* allow other pending transactions to proceed */
if (!response_in_progress) {
SC_REPORT_FATAL("TLM-2", "Illegal transaction phase END_RESP"
"received by target");
}
transaction_in_progress = 0;
/* Target itself is now clear to issue the next BEGIN_RESP */
response_in_progress = false;
if (next_response_pending) {
send_response( *next_response_pending );
next_response_pending = 0;
}
/* ... and to unblock the initiator by issuing END_REQ */
if (end_req_pending) {
send_end_req( *end_req_pending );
end_req_pending = 0;
}
} else /* tlm::END_REQ or tlm::BEGIN_RESP */ {
SC_REPORT_FATAL("TLM-2", "Illegal transaction phase received by"
"target");
}
}
void
Target::send_end_req(tlm::tlm_generic_payload& trans)
{
tlm::tlm_phase bw_phase;
sc_time delay;
/* Queue the acceptance and the response with the appropriate latency */
bw_phase = tlm::END_REQ;
delay = sc_time(10, SC_NS); // Accept delay
tlm::tlm_sync_enum status;
status = socket->nb_transport_bw(trans, bw_phase, delay);
/* Ignore return value;
* initiator cannot terminate transaction at this point
* Queue internal event to mark beginning of response: */
delay = delay + sc_time(40, SC_NS); // Latency
target_done_event.notify(delay);
assert(transaction_in_progress == 0);
transaction_in_progress = &trans;
}
void
Target::execute_transaction_process()
{
/* Execute the read or write commands */
execute_transaction( *transaction_in_progress );
/* Target must honor BEGIN_RESP/END_RESP exclusion rule; i.e. must not
* send BEGIN_RESP until receiving previous END_RESP or BEGIN_REQ */
if (response_in_progress) {
/* Target allows only two transactions in-flight */
if (next_response_pending) {
SC_REPORT_FATAL("TLM-2", "Attempt to have two pending responses"
"in target");
}
next_response_pending = transaction_in_progress;
} else {
send_response( *transaction_in_progress );
}
}
void
Target::execute_transaction(tlm::tlm_generic_payload& trans)
{
tlm::tlm_command cmd = trans.get_command();
sc_dt::uint64 adr = trans.get_address() - offset;
unsigned char* ptr = trans.get_data_ptr();
unsigned int len = trans.get_data_length();
unsigned char* byt = trans.get_byte_enable_ptr();
unsigned int wid = trans.get_streaming_width();
if ( byt != 0 ) {
cout << "Byte Error" << endl;
trans.set_response_status( tlm::TLM_BYTE_ENABLE_ERROR_RESPONSE );
return;
}
//if ( len > 4 || wid < len ) {
// cout << "Burst Error len=" << len << " wid=" << wid << endl;
// trans.set_response_status( tlm::TLM_BURST_ERROR_RESPONSE );
// return;
//}
unsigned char *mem_array_ptr = mem + adr;
/* Load / Store the access: */
if ( cmd == tlm::TLM_READ_COMMAND ) {
if(debug) {
SC_REPORT_INFO("target", "tlm::TLM_READ_COMMAND");
}
std::memcpy(ptr, mem_array_ptr, len);
} else if ( cmd == tlm::TLM_WRITE_COMMAND ) {
if(debug) {
SC_REPORT_INFO("target", "tlm::TLM_WRITE_COMMAND");
}
std::memcpy(mem_array_ptr, ptr, len);
}
trans.set_response_status( tlm::TLM_OK_RESPONSE );
}
void
Target::send_response(tlm::tlm_generic_payload& trans)
{
tlm::tlm_sync_enum status;
tlm::tlm_phase bw_phase;
sc_time delay;
response_in_progress = true;
bw_phase = tlm::BEGIN_RESP;
delay = sc_time(10, SC_NS);
status = socket->nb_transport_bw( trans, bw_phase, delay );
if (status == tlm::TLM_UPDATED) {
/* The timing annotation must be honored */
m_peq.notify(trans, bw_phase, delay);
} else if (status == tlm::TLM_COMPLETED) {
/* The initiator has terminated the transaction */
transaction_in_progress = 0;
response_in_progress = false;
}
trans.release();
}