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