1/*
2 * Copyright (c) 2015, University of Kaiserslautern
3 * Copyright (c) 2016, Dresden University of Technology (TU Dresden)
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice,
11 *    this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the copyright holder nor the names of its
18 *    contributors may be used to endorse or promote products derived from
19 *    this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
25 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 * Authors: Matthias Jung
34 *          Abdul Mutaal Ahmad
35 *          Christian Menard
36 */
37
38#include "sc_ext.hh"
39#include "sc_mm.hh"
40#include "sc_slave_port.hh"
41#include "slave_transactor.hh"
42
43namespace Gem5SystemC
44{
45
46/**
47 * Instantiate a tlm memory manager that takes care about all the
48 * tlm transactions in the system
49 */
50MemoryManager mm;
51
52/**
53 * Convert a gem5 packet to a TLM payload by copying all the relevant
54 * information to a previously allocated tlm payload
55 */
56void
57packet2payload(PacketPtr packet, tlm::tlm_generic_payload &trans)
58{
59    trans.set_address(packet->getAddr());
60
61    /* Check if this transaction was allocated by mm */
62    sc_assert(trans.has_mm());
63
64    unsigned int size = packet->getSize();
65    unsigned char *data = packet->getPtr<unsigned char>();
66
67    trans.set_data_length(size);
68    trans.set_streaming_width(size);
69    trans.set_data_ptr(data);
70
71    if (packet->isRead()) {
72        trans.set_command(tlm::TLM_READ_COMMAND);
73    }
74    else if (packet->isInvalidate()) {
75        /* Do nothing */
76    } else if (packet->isWrite()) {
77        trans.set_command(tlm::TLM_WRITE_COMMAND);
78    } else {
79        SC_REPORT_FATAL("SCSlavePort", "No R/W packet");
80    }
81}
82
83/**
84 * Similar to TLM's blocking transport (LT)
85 */
86Tick
87SCSlavePort::recvAtomic(PacketPtr packet)
88{
89    CAUGHT_UP;
90    SC_REPORT_INFO("SCSlavePort", "recvAtomic hasn't been tested much");
91
92    panic_if(packet->cacheResponding(), "Should not see packets where cache "
93             "is responding");
94
95    panic_if(!(packet->isRead() || packet->isWrite()),
96             "Should only see read and writes at TLM memory\n");
97
98
99    sc_core::sc_time delay = sc_core::SC_ZERO_TIME;
100
101
102    /* Prepare the transaction */
103    tlm::tlm_generic_payload * trans = mm.allocate();
104    trans->acquire();
105    packet2payload(packet, *trans);
106
107    /* Attach the packet pointer to the TLM transaction to keep track */
108    Gem5Extension* extension = new Gem5Extension(packet);
109    trans->set_auto_extension(extension);
110
111    /* Execute b_transport: */
112    if (packet->cmd == MemCmd::SwapReq) {
113        SC_REPORT_FATAL("SCSlavePort", "SwapReq not supported");
114    } else if (packet->isRead()) {
115        transactor->socket->b_transport(*trans, delay);
116    } else if (packet->isInvalidate()) {
117        // do nothing
118    } else if (packet->isWrite()) {
119        transactor->socket->b_transport(*trans, delay);
120    } else {
121        SC_REPORT_FATAL("SCSlavePort", "Typo of request not supported");
122    }
123
124    if (packet->needsResponse()) {
125        packet->makeResponse();
126    }
127
128    trans->release();
129
130    return delay.value();
131}
132
133/**
134 * Similar to TLM's debug transport
135 */
136void
137SCSlavePort::recvFunctional(PacketPtr packet)
138{
139    /* Prepare the transaction */
140    tlm::tlm_generic_payload * trans = mm.allocate();
141    trans->acquire();
142    packet2payload(packet, *trans);
143
144    /* Attach the packet pointer to the TLM transaction to keep track */
145    Gem5Extension* extension = new Gem5Extension(packet);
146    trans->set_auto_extension(extension);
147
148    /* Execute Debug Transport: */
149    unsigned int bytes = transactor->socket->transport_dbg(*trans);
150    if (bytes != trans->get_data_length()) {
151        SC_REPORT_FATAL("SCSlavePort","debug transport was not completed");
152    }
153
154    trans->release();
155}
156
157bool
158SCSlavePort::recvTimingSnoopResp(PacketPtr packet)
159{
160    /* Snooping should be implemented with tlm_dbg_transport */
161    SC_REPORT_FATAL("SCSlavePort","unimplemented func.: recvTimingSnoopResp");
162    return false;
163}
164
165void
166SCSlavePort::recvFunctionalSnoop(PacketPtr packet)
167{
168    /* Snooping should be implemented with tlm_dbg_transport */
169    SC_REPORT_FATAL("SCSlavePort","unimplemented func.: recvFunctionalSnoop");
170}
171
172/**
173 *  Similar to TLM's non-blocking transport (AT)
174 */
175bool
176SCSlavePort::recvTimingReq(PacketPtr packet)
177{
178    CAUGHT_UP;
179
180    panic_if(packet->cacheResponding(), "Should not see packets where cache "
181             "is responding");
182
183    panic_if(!(packet->isRead() || packet->isWrite()),
184             "Should only see read and writes at TLM memory\n");
185
186
187    /* We should never get a second request after noting that a retry is
188     * required */
189    sc_assert(!needToSendRequestRetry);
190
191    /* Remember if a request comes in while we're blocked so that a retry
192     * can be sent to gem5 */
193    if (blockingRequest) {
194        needToSendRequestRetry = true;
195        return false;
196    }
197
198    /*  NOTE: normal tlm is blocking here. But in our case we return false
199     *  and tell gem5 when a retry can be done. This is the main difference
200     *  in the protocol:
201     *  if (requestInProgress)
202     *  {
203     *      wait(endRequestEvent);
204     *  }
205     *  requestInProgress = trans;
206    */
207
208    /* Prepare the transaction */
209    tlm::tlm_generic_payload * trans = mm.allocate();
210    trans->acquire();
211    packet2payload(packet, *trans);
212
213    /* Attach the packet pointer to the TLM transaction to keep track */
214    Gem5Extension* extension = new Gem5Extension(packet);
215    trans->set_auto_extension(extension);
216
217    /*
218     * Pay for annotated transport delays.
219     *
220     * The header delay marks the point in time, when the packet first is seen
221     * by the transactor. This is the point int time, when the transactor needs
222     * to send the BEGIN_REQ to the SystemC world.
223     *
224     * NOTE: We drop the payload delay here. Normally, the receiver would be
225     *       responsible for handling the payload delay. In this case, however,
226     *       the receiver is a SystemC module and has no notion of the gem5
227     *       transport protocol and we cannot simply forward the
228     *       payload delay to the receiving module. Instead, we expect the
229     *       receiving SystemC module to model the payload delay by deferring
230     *       the END_REQ. This could lead to incorrect delays, if the XBar
231     *       payload delay is longer than the time the receiver needs to accept
232     *       the request (time between BEGIN_REQ and END_REQ).
233     *
234     * TODO: We could detect the case described above by remembering the
235     *       payload delay and comparing it to the time between BEGIN_REQ and
236     *       END_REQ. Then, a warning should be printed.
237     */
238    auto delay = sc_core::sc_time::from_value(packet->payloadDelay);
239    // reset the delays
240    packet->payloadDelay = 0;
241    packet->headerDelay = 0;
242
243    /* Starting TLM non-blocking sequence (AT) Refer to IEEE1666-2011 SystemC
244     * Standard Page 507 for a visualisation of the procedure */
245    tlm::tlm_phase phase = tlm::BEGIN_REQ;
246    tlm::tlm_sync_enum status;
247    status = transactor->socket->nb_transport_fw(*trans, phase, delay);
248    /* Check returned value: */
249    if (status == tlm::TLM_ACCEPTED) {
250        sc_assert(phase == tlm::BEGIN_REQ);
251        /* Accepted but is now blocking until END_REQ (exclusion rule)*/
252        blockingRequest = trans;
253    } else if (status == tlm::TLM_UPDATED) {
254        /* The Timing annotation must be honored: */
255        sc_assert(phase == tlm::END_REQ || phase == tlm::BEGIN_RESP);
256
257        PayloadEvent<SCSlavePort> * pe;
258        pe = new PayloadEvent<SCSlavePort>(*this,
259            &SCSlavePort::pec, "PEQ");
260        pe->notify(*trans, phase, delay);
261    } else if (status == tlm::TLM_COMPLETED) {
262        /* Transaction is over nothing has do be done. */
263        sc_assert(phase == tlm::END_RESP);
264        trans->release();
265    }
266
267    return true;
268}
269
270void
271SCSlavePort::pec(
272    PayloadEvent<SCSlavePort> * pe,
273    tlm::tlm_generic_payload& trans,
274    const tlm::tlm_phase& phase)
275{
276    sc_time delay;
277
278    if (phase == tlm::END_REQ ||
279            &trans == blockingRequest && phase == tlm::BEGIN_RESP) {
280        sc_assert(&trans == blockingRequest);
281        blockingRequest = NULL;
282
283        /* Did another request arrive while blocked, schedule a retry */
284        if (needToSendRequestRetry) {
285            needToSendRequestRetry = false;
286            sendRetryReq();
287        }
288    }
289    if (phase == tlm::BEGIN_RESP)
290    {
291        CAUGHT_UP;
292
293        auto& extension = Gem5Extension::getExtension(trans);
294        auto packet = extension.getPacket();
295
296        sc_assert(!blockingResponse);
297
298        bool need_retry = false;
299
300        /*
301         * If the packet was piped through and needs a response, we don't need
302         * to touch the packet and can forward it directly as a response.
303         * Otherwise, we need to make a response and send the transformed
304         * packet.
305         */
306        if (extension.isPipeThrough()) {
307            if (packet->isResponse()) {
308                need_retry = !sendTimingResp(packet);
309            }
310        } else if (packet->needsResponse()) {
311            packet->makeResponse();
312            need_retry = !sendTimingResp(packet);
313        }
314
315        if (need_retry) {
316            blockingResponse = &trans;
317        } else {
318            if (phase == tlm::BEGIN_RESP) {
319                /* Send END_RESP and we're finished: */
320                tlm::tlm_phase fw_phase = tlm::END_RESP;
321                sc_time delay = SC_ZERO_TIME;
322                transactor->socket->nb_transport_fw(trans, fw_phase, delay);
323                /* Release the transaction with all the extensions */
324                trans.release();
325            }
326        }
327    }
328    delete pe;
329}
330
331void
332SCSlavePort::recvRespRetry()
333{
334    CAUGHT_UP;
335
336    /* Retry a response */
337    sc_assert(blockingResponse);
338
339    tlm::tlm_generic_payload *trans = blockingResponse;
340    blockingResponse = NULL;
341    PacketPtr packet = Gem5Extension::getExtension(trans).getPacket();
342
343    bool need_retry = !sendTimingResp(packet);
344
345    sc_assert(!need_retry);
346
347    sc_core::sc_time delay = sc_core::SC_ZERO_TIME;
348    tlm::tlm_phase phase = tlm::END_RESP;
349    transactor->socket->nb_transport_fw(*trans, phase, delay);
350    // Release transaction with all the extensions
351    trans->release();
352}
353
354tlm::tlm_sync_enum
355SCSlavePort::nb_transport_bw(tlm::tlm_generic_payload& trans,
356    tlm::tlm_phase& phase,
357    sc_core::sc_time& delay)
358{
359    PayloadEvent<SCSlavePort> * pe;
360    pe = new PayloadEvent<SCSlavePort>(*this, &SCSlavePort::pec, "PE");
361    pe->notify(trans, phase, delay);
362    return tlm::TLM_ACCEPTED;
363}
364
365SCSlavePort::SCSlavePort(const std::string &name_,
366    const std::string &systemc_name,
367    ExternalSlave &owner_) :
368    ExternalSlave::Port(name_, owner_),
369    blockingRequest(NULL),
370    needToSendRequestRetry(false),
371    blockingResponse(NULL),
372    transactor(nullptr)
373{
374}
375
376void
377SCSlavePort::bindToTransactor(Gem5SlaveTransactor* transactor)
378{
379    sc_assert(this->transactor == nullptr);
380
381    this->transactor = transactor;
382
383    transactor->socket.register_nb_transport_bw(this,
384                                                &SCSlavePort::nb_transport_bw);
385}
386
387ExternalSlave::Port*
388SCSlavePortHandler::getExternalPort(const std::string &name,
389                                    ExternalSlave &owner,
390                                    const std::string &port_data)
391{
392    // Create and register a new SystemC slave port
393    auto* port = new SCSlavePort(name, port_data, owner);
394
395    control.registerSlavePort(port_data, port);
396
397    return port;
398}
399
400}
401