1/*
2 * Copyright (c) 2016, Dresden University of Technology (TU Dresden)
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * 1. Redistributions of source code must retain the above copyright notice,
10 *    this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * 3. Neither the name of the copyright holder nor the names of its
17 *    contributors may be used to endorse or promote products derived from
18 *    this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
24 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 * Authors: Christian Menard
33 */
34
35#include <sstream>
36
37#include "master_transactor.hh"
38#include "params/ExternalMaster.hh"
39#include "sc_ext.hh"
40#include "sc_master_port.hh"
41#include "sim/system.hh"
42
43namespace Gem5SystemC
44{
45
46PacketPtr
47SCMasterPort::generatePacket(tlm::tlm_generic_payload& trans)
48{
49    Request::Flags flags;
50    auto req = std::make_shared<Request>(
51        trans.get_address(), trans.get_data_length(), flags,
52        owner.masterId);
53
54    MemCmd cmd;
55
56    switch (trans.get_command()) {
57        case tlm::TLM_READ_COMMAND:
58            cmd = MemCmd::ReadReq;
59            break;
60        case tlm::TLM_WRITE_COMMAND:
61            cmd = MemCmd::WriteReq;
62            break;
63        default:
64            SC_REPORT_FATAL("SCMasterPort",
65                            "received transaction with unsupported command");
66    }
67
68    /*
69     * Allocate a new Packet. The packet will be deleted when it returns from
70     * the gem5 world as a response.
71     */
72    auto pkt = new Packet(req, cmd);
73    pkt->dataStatic(trans.get_data_ptr());
74
75    return pkt;
76}
77
78void
79SCMasterPort::destroyPacket(PacketPtr pkt)
80{
81    delete pkt;
82}
83
84SCMasterPort::SCMasterPort(const std::string& name_,
85                           const std::string& systemc_name,
86                           ExternalMaster& owner_,
87                           Gem5SimControl& simControl)
88  : ExternalMaster::Port(name_, owner_),
89    peq(this, &SCMasterPort::peq_cb),
90    waitForRetry(false),
91    pendingRequest(nullptr),
92    pendingPacket(nullptr),
93    needToSendRetry(false),
94    responseInProgress(false),
95    transactor(nullptr),
96    simControl(simControl)
97{
98    system =
99        dynamic_cast<const ExternalMasterParams*>(owner_.params())->system;
100}
101
102void
103SCMasterPort::bindToTransactor(Gem5MasterTransactor* transactor)
104{
105    sc_assert(this->transactor == nullptr);
106
107    this->transactor = transactor;
108
109    /*
110     * Register the TLM non-blocking interface when using gem5 Timing mode and
111     * the TLM blocking interface when using the gem5 Atomic mode.
112     * Then the magic (TM) in simple_target_socket automatically transforms
113     * non-blocking in blocking transactions and vice versa.
114     *
115     * NOTE: The mode may change during execution.
116     */
117    if (system->isTimingMode()) {
118        SC_REPORT_INFO("SCMasterPort", "register non-blocking interface");
119        transactor->socket.register_nb_transport_fw(this,
120                                &SCMasterPort::nb_transport_fw);
121    } else if (system->isAtomicMode()) {
122        SC_REPORT_INFO("SCMasterPort", "register blocking interface");
123        transactor->socket.register_b_transport(this,
124                                &SCMasterPort::b_transport);
125    } else {
126        panic("gem5 operates neither in Timing nor in Atomic mode");
127    }
128
129    transactor->socket.register_transport_dbg(this,
130                                              &SCMasterPort::transport_dbg);
131}
132
133void
134SCMasterPort::checkTransaction(tlm::tlm_generic_payload& trans)
135{
136    if (trans.is_response_error()) {
137        std::stringstream ss;
138        ss << "Transaction returned with error, response status = "
139           << trans.get_response_string();
140        SC_REPORT_ERROR("TLM-2", ss.str().c_str());
141    }
142}
143
144tlm::tlm_sync_enum
145SCMasterPort::nb_transport_fw(tlm::tlm_generic_payload& trans,
146                              tlm::tlm_phase& phase, sc_core::sc_time& delay)
147{
148    uint64_t adr = trans.get_address();
149    unsigned len = trans.get_data_length();
150    unsigned char* byteEnable = trans.get_byte_enable_ptr();
151    unsigned width = trans.get_streaming_width();
152
153    // check the transaction attributes for unsupported features ...
154    if (byteEnable != 0) {
155        trans.set_response_status(tlm::TLM_BYTE_ENABLE_ERROR_RESPONSE);
156        return tlm::TLM_COMPLETED;
157    }
158    if (width < len) { // is this a burst request?
159        trans.set_response_status(tlm::TLM_BURST_ERROR_RESPONSE);
160        return tlm::TLM_COMPLETED;
161    }
162
163    // ... and queue the valid transaction
164    trans.acquire();
165    peq.notify(trans, phase, delay);
166    return tlm::TLM_ACCEPTED;
167}
168
169void
170SCMasterPort::peq_cb(tlm::tlm_generic_payload& trans,
171                       const tlm::tlm_phase& phase)
172{
173    // catch up with SystemC time
174    simControl.catchup();
175    assert(curTick() == sc_core::sc_time_stamp().value());
176
177    switch (phase) {
178        case tlm::BEGIN_REQ:
179            handleBeginReq(trans);
180            break;
181        case tlm::END_RESP:
182            handleEndResp(trans);
183            break;
184        default:
185            panic("unimplemented phase in callback");
186    }
187
188    // the functions called above may have scheduled gem5 events
189    // -> notify the event loop handler
190    simControl.notify();
191}
192
193void
194SCMasterPort::handleBeginReq(tlm::tlm_generic_payload& trans)
195{
196    sc_assert(!waitForRetry);
197    sc_assert(pendingRequest == nullptr);
198    sc_assert(pendingPacket == nullptr);
199
200    trans.acquire();
201
202    PacketPtr pkt = nullptr;
203
204    Gem5Extension* extension = nullptr;
205    trans.get_extension(extension);
206
207    // If there is an extension, this transaction was initiated by the gem5
208    // world and we can pipe through the original packet. Otherwise, we
209    // generate a new packet based on the transaction.
210    if (extension != nullptr) {
211        extension->setPipeThrough();
212        pkt = extension->getPacket();
213    } else {
214        pkt = generatePacket(trans);
215    }
216
217    auto tlmSenderState = new TlmSenderState(trans);
218    pkt->pushSenderState(tlmSenderState);
219
220    if (sendTimingReq(pkt)) { // port is free -> send END_REQ immediately
221        sendEndReq(trans);
222        trans.release();
223    } else { // port is blocked -> wait for retry before sending END_REQ
224        waitForRetry = true;
225        pendingRequest = &trans;
226        pendingPacket = pkt;
227    }
228}
229
230void
231SCMasterPort::handleEndResp(tlm::tlm_generic_payload& trans)
232{
233    sc_assert(responseInProgress);
234
235    responseInProgress = false;
236
237    checkTransaction(trans);
238
239    if (needToSendRetry) {
240        sendRetryResp();
241        needToSendRetry = false;
242    }
243}
244
245void
246SCMasterPort::sendEndReq(tlm::tlm_generic_payload& trans)
247{
248    tlm::tlm_phase phase = tlm::END_REQ;
249    auto delay = sc_core::SC_ZERO_TIME;
250
251    auto status = transactor->socket->nb_transport_bw(trans, phase, delay);
252    panic_if(status != tlm::TLM_ACCEPTED,
253             "Unexpected status after sending END_REQ");
254}
255
256void
257SCMasterPort::b_transport(tlm::tlm_generic_payload& trans,
258                        sc_core::sc_time& t)
259{
260    Gem5Extension* extension = nullptr;
261    trans.get_extension(extension);
262
263    PacketPtr pkt = nullptr;
264
265    // If there is an extension, this transaction was initiated by the gem5
266    // world and we can pipe through the original packet.
267    if (extension != nullptr) {
268        extension->setPipeThrough();
269        pkt = extension->getPacket();
270    } else {
271        pkt = generatePacket(trans);
272    }
273
274    Tick ticks = sendAtomic(pkt);
275
276    // send an atomic request to gem5
277    panic_if(pkt->needsResponse() && !pkt->isResponse(),
278             "Packet sending failed!\n");
279
280    // one tick is a pico second
281    auto delay =
282      sc_core::sc_time((double)(ticks / SimClock::Int::ps), sc_core::SC_PS);
283
284    // update time
285    t += delay;
286
287    if (extension == nullptr)
288        destroyPacket(pkt);
289
290    trans.set_response_status(tlm::TLM_OK_RESPONSE);
291}
292
293unsigned int
294SCMasterPort::transport_dbg(tlm::tlm_generic_payload& trans)
295{
296    Gem5Extension* extension = nullptr;
297    trans.get_extension(extension);
298
299    // If there is an extension, this transaction was initiated by the gem5
300    // world and we can pipe through the original packet.
301    if (extension != nullptr) {
302        extension->setPipeThrough();
303        sendFunctional(extension->getPacket());
304    } else {
305        auto pkt = generatePacket(trans);
306        sendFunctional(pkt);
307        destroyPacket(pkt);
308    }
309
310    return trans.get_data_length();
311}
312
313bool
314SCMasterPort::get_direct_mem_ptr(tlm::tlm_generic_payload& trans,
315                               tlm::tlm_dmi& dmi_data)
316{
317    return false;
318}
319
320bool
321SCMasterPort::recvTimingResp(PacketPtr pkt)
322{
323    // exclusion rule
324    // We need to Wait for END_RESP before sending next BEGIN_RESP
325    if (responseInProgress) {
326        sc_assert(!needToSendRetry);
327        needToSendRetry = true;
328        return false;
329    }
330
331    sc_assert(pkt->isResponse());
332
333    /*
334     * Pay for annotated transport delays.
335     *
336     * See recvTimingReq in sc_slave_port.cc for a detailed description.
337     */
338    auto delay = sc_core::sc_time::from_value(pkt->payloadDelay);
339    // reset the delays
340    pkt->payloadDelay = 0;
341    pkt->headerDelay = 0;
342
343    auto tlmSenderState = dynamic_cast<TlmSenderState*>(pkt->popSenderState());
344    sc_assert(tlmSenderState != nullptr);
345
346    auto& trans = tlmSenderState->trans;
347
348    Gem5Extension* extension = nullptr;
349    trans.get_extension(extension);
350
351    // clean up
352    delete tlmSenderState;
353
354    // If there is an extension the packet was piped through and we must not
355    // delete it. The packet travels back with the transaction.
356    if (extension == nullptr)
357        destroyPacket(pkt);
358    else
359        sc_assert(extension->isPipeThrough());
360
361    sendBeginResp(trans, delay);
362    trans.release();
363
364    return true;
365}
366
367void
368SCMasterPort::sendBeginResp(tlm::tlm_generic_payload& trans,
369                            sc_core::sc_time& delay)
370{
371    tlm::tlm_phase phase = tlm::BEGIN_RESP;
372
373    trans.set_response_status(tlm::TLM_OK_RESPONSE);
374
375    auto status = transactor->socket->nb_transport_bw(trans, phase, delay);
376
377    if (status == tlm::TLM_COMPLETED ||
378        status == tlm::TLM_UPDATED && phase == tlm::END_RESP) {
379        // transaction completed -> no need to wait for tlm::END_RESP
380        responseInProgress = false;
381    } else if (status == tlm::TLM_ACCEPTED) {
382        // we need to wait for tlm::END_RESP
383        responseInProgress = true;
384    } else {
385        panic("Unexpected status after sending BEGIN_RESP");
386    }
387}
388
389void
390SCMasterPort::recvReqRetry()
391{
392    sc_assert(waitForRetry);
393    sc_assert(pendingRequest != nullptr);
394    sc_assert(pendingPacket != nullptr);
395
396    if (sendTimingReq(pendingPacket)) {
397        waitForRetry = false;
398        pendingPacket = nullptr;
399
400        auto& trans = *pendingRequest;
401        sendEndReq(trans);
402        trans.release();
403
404        pendingRequest = nullptr;
405    }
406}
407
408void
409SCMasterPort::recvRangeChange()
410{
411    SC_REPORT_WARNING("SCMasterPort",
412                      "received address range change but ignored it");
413}
414
415ExternalMaster::Port*
416SCMasterPortHandler::getExternalPort(const std::string &name,
417                                     ExternalMaster &owner,
418                                     const std::string &port_data)
419{
420    // Create and register a new SystemC master port
421    auto* port = new SCMasterPort(name, port_data, owner, control);
422
423    control.registerMasterPort(port_data, port);
424
425    return port;
426}
427
428} // namespace Gem5SystemC
429