sc_master_port.cc revision 12047
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 = new Request(trans.get_address(), trans.get_data_length(), flags,
51                           owner.masterId);
52
53    MemCmd cmd;
54
55    switch (trans.get_command()) {
56        case tlm::TLM_READ_COMMAND:
57            cmd = MemCmd::ReadReq;
58            break;
59        case tlm::TLM_WRITE_COMMAND:
60            cmd = MemCmd::WriteReq;
61            break;
62        default:
63            SC_REPORT_FATAL("SCMasterPort",
64                            "received transaction with unsupported command");
65    }
66
67    /*
68     * Allocate a new Packet. The packet will be deleted when it returns from
69     * the gem5 world as a response.
70     */
71    auto pkt = new Packet(req, cmd);
72    pkt->dataStatic(trans.get_data_ptr());
73
74    return pkt;
75}
76
77void
78SCMasterPort::destroyPacket(PacketPtr pkt)
79{
80    delete pkt;
81}
82
83SCMasterPort::SCMasterPort(const std::string& name_,
84                           const std::string& systemc_name,
85                           ExternalMaster& owner_,
86                           Gem5SimControl& simControl)
87  : ExternalMaster::Port(name_, owner_),
88    peq(this, &SCMasterPort::peq_cb),
89    waitForRetry(false),
90    pendingRequest(nullptr),
91    pendingPacket(nullptr),
92    needToSendRetry(false),
93    responseInProgress(false),
94    transactor(nullptr),
95    simControl(simControl)
96{
97    system =
98        dynamic_cast<const ExternalMasterParams*>(owner_.params())->system;
99}
100
101void
102SCMasterPort::bindToTransactor(Gem5MasterTransactor* transactor)
103{
104    sc_assert(this->transactor == nullptr);
105
106    this->transactor = transactor;
107
108    /*
109     * Register the TLM non-blocking interface when using gem5 Timing mode and
110     * the TLM blocking interface when using the gem5 Atomic mode.
111     * Then the magic (TM) in simple_target_socket automatically transforms
112     * non-blocking in blocking transactions and vice versa.
113     *
114     * NOTE: The mode may change during execution.
115     */
116    if (system->isTimingMode()) {
117        SC_REPORT_INFO("SCMasterPort", "register non-blocking interface");
118        transactor->socket.register_nb_transport_fw(this,
119                                &SCMasterPort::nb_transport_fw);
120    } else if (system->isAtomicMode()) {
121        SC_REPORT_INFO("SCMasterPort", "register blocking interface");
122        transactor->socket.register_b_transport(this,
123                                &SCMasterPort::b_transport);
124    } else {
125        panic("gem5 operates neither in Timing nor in Atomic mode");
126    }
127
128    transactor->socket.register_transport_dbg(this,
129                                              &SCMasterPort::transport_dbg);
130}
131
132void
133SCMasterPort::checkTransaction(tlm::tlm_generic_payload& trans)
134{
135    if (trans.is_response_error()) {
136        std::stringstream ss;
137        ss << "Transaction returned with error, response status = "
138           << trans.get_response_string();
139        SC_REPORT_ERROR("TLM-2", ss.str().c_str());
140    }
141}
142
143tlm::tlm_sync_enum
144SCMasterPort::nb_transport_fw(tlm::tlm_generic_payload& trans,
145                              tlm::tlm_phase& phase, sc_core::sc_time& delay)
146{
147    uint64_t adr = trans.get_address();
148    unsigned len = trans.get_data_length();
149    unsigned char* byteEnable = trans.get_byte_enable_ptr();
150    unsigned width = trans.get_streaming_width();
151
152    // check the transaction attributes for unsupported features ...
153    if (byteEnable != 0) {
154        trans.set_response_status(tlm::TLM_BYTE_ENABLE_ERROR_RESPONSE);
155        return tlm::TLM_COMPLETED;
156    }
157    if (width < len) { // is this a burst request?
158        trans.set_response_status(tlm::TLM_BURST_ERROR_RESPONSE);
159        return tlm::TLM_COMPLETED;
160    }
161
162    // ... and queue the valid transaction
163    trans.acquire();
164    peq.notify(trans, phase, delay);
165    return tlm::TLM_ACCEPTED;
166}
167
168void
169SCMasterPort::peq_cb(tlm::tlm_generic_payload& trans,
170                       const tlm::tlm_phase& phase)
171{
172    // catch up with SystemC time
173    simControl.catchup();
174    assert(curTick() == sc_core::sc_time_stamp().value());
175
176    switch (phase) {
177        case tlm::BEGIN_REQ:
178            handleBeginReq(trans);
179            break;
180        case tlm::END_RESP:
181            handleEndResp(trans);
182            break;
183        default:
184            panic("unimplemented phase in callback");
185    }
186
187    // the functions called above may have scheduled gem5 events
188    // -> notify the event loop handler
189    simControl.notify();
190}
191
192void
193SCMasterPort::handleBeginReq(tlm::tlm_generic_payload& trans)
194{
195    sc_assert(!waitForRetry);
196    sc_assert(pendingRequest == nullptr);
197    sc_assert(pendingPacket == nullptr);
198
199    trans.acquire();
200
201    PacketPtr pkt = nullptr;
202
203    Gem5Extension* extension = nullptr;
204    trans.get_extension(extension);
205
206    // If there is an extension, this transaction was initiated by the gem5
207    // world and we can pipe through the original packet. Otherwise, we
208    // generate a new packet based on the transaction.
209    if (extension != nullptr) {
210        extension->setPipeThrough();
211        pkt = extension->getPacket();
212    } else {
213        pkt = generatePacket(trans);
214    }
215
216    auto tlmSenderState = new TlmSenderState(trans);
217    pkt->pushSenderState(tlmSenderState);
218
219    if (sendTimingReq(pkt)) { // port is free -> send END_REQ immediately
220        sendEndReq(trans);
221        trans.release();
222    } else { // port is blocked -> wait for retry before sending END_REQ
223        waitForRetry = true;
224        pendingRequest = &trans;
225        pendingPacket = pkt;
226    }
227}
228
229void
230SCMasterPort::handleEndResp(tlm::tlm_generic_payload& trans)
231{
232    sc_assert(responseInProgress);
233
234    responseInProgress = false;
235
236    checkTransaction(trans);
237
238    if (needToSendRetry) {
239        sendRetryResp();
240        needToSendRetry = false;
241    }
242}
243
244void
245SCMasterPort::sendEndReq(tlm::tlm_generic_payload& trans)
246{
247    tlm::tlm_phase phase = tlm::END_REQ;
248    auto delay = sc_core::SC_ZERO_TIME;
249
250    auto status = transactor->socket->nb_transport_bw(trans, phase, delay);
251    panic_if(status != tlm::TLM_ACCEPTED,
252             "Unexpected status after sending END_REQ");
253}
254
255void
256SCMasterPort::b_transport(tlm::tlm_generic_payload& trans,
257                        sc_core::sc_time& t)
258{
259    Gem5Extension* extension = nullptr;
260    trans.get_extension(extension);
261
262    PacketPtr pkt = nullptr;
263
264    // If there is an extension, this transaction was initiated by the gem5
265    // world and we can pipe through the original packet.
266    if (extension != nullptr) {
267        extension->setPipeThrough();
268        pkt = extension->getPacket();
269    } else {
270        pkt = generatePacket(trans);
271    }
272
273    Tick ticks = sendAtomic(pkt);
274
275    // send an atomic request to gem5
276    panic_if(pkt->needsResponse() && !pkt->isResponse(),
277             "Packet sending failed!\n");
278
279    // one tick is a pico second
280    auto delay =
281      sc_core::sc_time((double)(ticks / SimClock::Int::ps), sc_core::SC_PS);
282
283    // update time
284    t += delay;
285
286    if (extension != nullptr)
287        destroyPacket(pkt);
288
289    trans.set_response_status(tlm::TLM_OK_RESPONSE);
290}
291
292unsigned int
293SCMasterPort::transport_dbg(tlm::tlm_generic_payload& trans)
294{
295    Gem5Extension* extension = nullptr;
296    trans.get_extension(extension);
297
298    // If there is an extension, this transaction was initiated by the gem5
299    // world and we can pipe through the original packet.
300    if (extension != nullptr) {
301        extension->setPipeThrough();
302        sendFunctional(extension->getPacket());
303    } else {
304        auto pkt = generatePacket(trans);
305        sendFunctional(pkt);
306        destroyPacket(pkt);
307    }
308
309    return trans.get_data_length();
310}
311
312bool
313SCMasterPort::get_direct_mem_ptr(tlm::tlm_generic_payload& trans,
314                               tlm::tlm_dmi& dmi_data)
315{
316    return false;
317}
318
319bool
320SCMasterPort::recvTimingResp(PacketPtr pkt)
321{
322    // exclusion rule
323    // We need to Wait for END_RESP before sending next BEGIN_RESP
324    if (responseInProgress) {
325        sc_assert(!needToSendRetry);
326        needToSendRetry = true;
327        return false;
328    }
329
330    sc_assert(pkt->isResponse());
331
332    /*
333     * Pay for annotated transport delays.
334     *
335     * See recvTimingReq in sc_slave_port.cc for a detailed description.
336     */
337    auto delay = sc_core::sc_time::from_value(pkt->payloadDelay);
338    // reset the delays
339    pkt->payloadDelay = 0;
340    pkt->headerDelay = 0;
341
342    auto tlmSenderState = dynamic_cast<TlmSenderState*>(pkt->popSenderState());
343    sc_assert(tlmSenderState != nullptr);
344
345    auto& trans = tlmSenderState->trans;
346
347    Gem5Extension* extension = nullptr;
348    trans.get_extension(extension);
349
350    // clean up
351    delete tlmSenderState;
352
353    // If there is an extension the packet was piped through and we must not
354    // delete it. The packet travels back with the transaction.
355    if (extension == nullptr)
356        destroyPacket(pkt);
357    else
358        sc_assert(extension->isPipeThrough());
359
360    sendBeginResp(trans, delay);
361    trans.release();
362
363    return true;
364}
365
366void
367SCMasterPort::sendBeginResp(tlm::tlm_generic_payload& trans,
368                            sc_core::sc_time& delay)
369{
370    tlm::tlm_phase phase = tlm::BEGIN_RESP;
371
372    trans.set_response_status(tlm::TLM_OK_RESPONSE);
373
374    auto status = transactor->socket->nb_transport_bw(trans, phase, delay);
375
376    if (status == tlm::TLM_COMPLETED ||
377        status == tlm::TLM_UPDATED && phase == tlm::END_RESP) {
378        // transaction completed -> no need to wait for tlm::END_RESP
379        responseInProgress = false;
380    } else if (status == tlm::TLM_ACCEPTED) {
381        // we need to wait for tlm::END_RESP
382        responseInProgress = true;
383    } else {
384        panic("Unexpected status after sending BEGIN_RESP");
385    }
386}
387
388void
389SCMasterPort::recvReqRetry()
390{
391    sc_assert(waitForRetry);
392    sc_assert(pendingRequest != nullptr);
393    sc_assert(pendingPacket != nullptr);
394
395    if (sendTimingReq(pendingPacket)) {
396        waitForRetry = false;
397        pendingPacket = nullptr;
398
399        auto& trans = *pendingRequest;
400        sendEndReq(trans);
401        trans.release();
402
403        pendingRequest = nullptr;
404    }
405}
406
407void
408SCMasterPort::recvRangeChange()
409{
410    SC_REPORT_WARNING("SCMasterPort",
411                      "received address range change but ignored it");
412}
413
414ExternalMaster::Port*
415SCMasterPortHandler::getExternalPort(const std::string &name,
416                                     ExternalMaster &owner,
417                                     const std::string &port_data)
418{
419    // Create and register a new SystemC master port
420    auto* port = new SCMasterPort(name, port_data, owner, control);
421
422    control.registerMasterPort(port_data, port);
423
424    return port;
425}
426
427} // namespace Gem5SystemC
428