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 "params/Gem5ToTlmBridge32.hh"
67#include "params/Gem5ToTlmBridge64.hh"
68#include "sim/system.hh"
69#include "systemc/tlm_bridge/sc_ext.hh"
70#include "systemc/tlm_bridge/sc_mm.hh"
71
72namespace sc_gem5
73{
74
75/**
76 * Instantiate a tlm memory manager that takes care about all the
77 * tlm transactions in the system.
78 */
79Gem5SystemC::MemoryManager mm;
80
81/**
82 * Convert a gem5 packet to a TLM payload by copying all the relevant
83 * information to new tlm payload.
84 */
85tlm::tlm_generic_payload *
86packet2payload(PacketPtr packet)
87{
88    tlm::tlm_generic_payload *trans = mm.allocate();
89    trans->acquire();
90
91    trans->set_address(packet->getAddr());
92
93    /* Check if this transaction was allocated by mm */
94    sc_assert(trans->has_mm());
95
96    unsigned int size = packet->getSize();
97    unsigned char *data = packet->getPtr<unsigned char>();
98
99    trans->set_data_length(size);
100    trans->set_streaming_width(size);
101    trans->set_data_ptr(data);
102
103    if ((packet->req->getFlags() & Request::NO_ACCESS) != 0) {
104        /* Do nothing */
105        trans->set_command(tlm::TLM_IGNORE_COMMAND);
106    } else if (packet->isRead()) {
107        trans->set_command(tlm::TLM_READ_COMMAND);
108    } else if (packet->isInvalidate()) {
109        /* Do nothing */
110        trans->set_command(tlm::TLM_IGNORE_COMMAND);
111    } else if (packet->isWrite()) {
112        trans->set_command(tlm::TLM_WRITE_COMMAND);
113    } else {
114        SC_REPORT_FATAL("Gem5ToTlmBridge", "No R/W packet");
115    }
116
117    // Attach the packet pointer to the TLM transaction to keep track.
118    auto *extension = new Gem5SystemC::Gem5Extension(packet);
119    trans->set_auto_extension(extension);
120
121    return trans;
122}
123
124template <unsigned int BITWIDTH>
125void
126Gem5ToTlmBridge<BITWIDTH>::pec(
127        Gem5SystemC::PayloadEvent<Gem5ToTlmBridge<BITWIDTH>> *pe,
128        tlm::tlm_generic_payload &trans, const tlm::tlm_phase &phase)
129{
130    sc_core::sc_time delay;
131
132    if (phase == tlm::END_REQ ||
133            (&trans == blockingRequest && phase == tlm::BEGIN_RESP)) {
134        sc_assert(&trans == blockingRequest);
135        blockingRequest = nullptr;
136
137        // Did another request arrive while blocked, schedule a retry.
138        if (needToSendRequestRetry) {
139            needToSendRequestRetry = false;
140            bsp.sendRetryReq();
141        }
142    }
143    if (phase == tlm::BEGIN_RESP) {
144        auto &extension = Gem5SystemC::Gem5Extension::getExtension(trans);
145        auto packet = extension.getPacket();
146
147        sc_assert(!blockingResponse);
148
149        bool need_retry = false;
150
151        /*
152         * If the packet was piped through and needs a response, we don't need
153         * to touch the packet and can forward it directly as a response.
154         * Otherwise, we need to make a response and send the transformed
155         * packet.
156         */
157        if (extension.isPipeThrough()) {
158            if (packet->isResponse()) {
159                need_retry = !bsp.sendTimingResp(packet);
160            }
161        } else if (packet->needsResponse()) {
162            packet->makeResponse();
163            need_retry = !bsp.sendTimingResp(packet);
164        }
165
166        if (need_retry) {
167            blockingResponse = &trans;
168        } else {
169            if (phase == tlm::BEGIN_RESP) {
170                // Send END_RESP and we're finished:
171                tlm::tlm_phase fw_phase = tlm::END_RESP;
172                sc_core::sc_time delay = sc_core::SC_ZERO_TIME;
173                socket->nb_transport_fw(trans, fw_phase, delay);
174                // Release the transaction with all the extensions.
175                trans.release();
176            }
177        }
178    }
179    delete pe;
180}
181
182template <unsigned int BITWIDTH>
183MemBackdoorPtr
184Gem5ToTlmBridge<BITWIDTH>::getBackdoor(tlm::tlm_generic_payload &trans)
185{
186    sc_dt::uint64 start = trans.get_address();
187    sc_dt::uint64 end = start + trans.get_data_length();
188
189    // Check for a back door we already know about.
190    AddrRange r(start, end);
191    auto it = backdoorMap.contains(r);
192    if (it != backdoorMap.end())
193        return it->second;
194
195    // If not, ask the target for one.
196    tlm::tlm_dmi dmi_data;
197    if (!socket->get_direct_mem_ptr(trans, dmi_data))
198        return nullptr;
199
200    // If the target gave us one, translate it to a gem5 MemBackdoor and
201    // store it in our cache.
202    AddrRange dmi_r(dmi_data.get_start_address(), dmi_data.get_end_address());
203    auto backdoor = new MemBackdoor(
204            dmi_r, dmi_data.get_dmi_ptr(), MemBackdoor::NoAccess);
205    backdoor->readable(dmi_data.is_read_allowed());
206    backdoor->writeable(dmi_data.is_write_allowed());
207
208    backdoorMap.insert(dmi_r, backdoor);
209
210    return backdoor;
211}
212
213// Similar to TLM's blocking transport (LT)
214template <unsigned int BITWIDTH>
215Tick
216Gem5ToTlmBridge<BITWIDTH>::recvAtomic(PacketPtr packet)
217{
218    panic_if(packet->cacheResponding(),
219             "Should not see packets where cache is responding");
220
221    // Prepare the transaction.
222    auto *trans = packet2payload(packet);
223
224    sc_core::sc_time delay = sc_core::SC_ZERO_TIME;
225
226    if (trans->get_command() != tlm::TLM_IGNORE_COMMAND) {
227        // Execute b_transport:
228        socket->b_transport(*trans, delay);
229    }
230
231    if (packet->needsResponse())
232        packet->makeResponse();
233
234    trans->release();
235
236    return delay.value();
237}
238
239template <unsigned int BITWIDTH>
240Tick
241Gem5ToTlmBridge<BITWIDTH>::recvAtomicBackdoor(
242        PacketPtr packet, MemBackdoorPtr &backdoor)
243{
244    panic_if(packet->cacheResponding(),
245             "Should not see packets where cache is responding");
246
247    sc_core::sc_time delay = sc_core::SC_ZERO_TIME;
248
249    // Prepare the transaction.
250    auto *trans = packet2payload(packet);
251
252    if (trans->get_command() != tlm::TLM_IGNORE_COMMAND) {
253        // Execute b_transport:
254        socket->b_transport(*trans, delay);
255        // If the hint said we could use DMI, set that up.
256        if (trans->is_dmi_allowed())
257            backdoor = getBackdoor(*trans);
258    } else {
259        // There's no transaction to piggy back on, so just request the
260        // backdoor normally.
261        backdoor = getBackdoor(*trans);
262    }
263
264    if (packet->needsResponse())
265        packet->makeResponse();
266
267    trans->release();
268
269    return delay.value();
270}
271
272template <unsigned int BITWIDTH>
273void
274Gem5ToTlmBridge<BITWIDTH>::recvFunctionalSnoop(PacketPtr packet)
275{
276    // Snooping should be implemented with tlm_dbg_transport.
277    SC_REPORT_FATAL("Gem5ToTlmBridge",
278            "unimplemented func.: recvFunctionalSnoop");
279}
280
281// Similar to TLM's non-blocking transport (AT).
282template <unsigned int BITWIDTH>
283bool
284Gem5ToTlmBridge<BITWIDTH>::recvTimingReq(PacketPtr packet)
285{
286    panic_if(packet->cacheResponding(),
287             "Should not see packets where cache is responding");
288
289    panic_if(!(packet->isRead() || packet->isWrite()),
290             "Should only see read and writes at TLM memory\n");
291
292
293    // We should never get a second request after noting that a retry is
294    // required.
295    sc_assert(!needToSendRequestRetry);
296
297    // Remember if a request comes in while we're blocked so that a retry
298    // can be sent to gem5.
299    if (blockingRequest) {
300        needToSendRequestRetry = true;
301        return false;
302    }
303
304    /*
305     * NOTE: normal tlm is blocking here. But in our case we return false
306     * and tell gem5 when a retry can be done. This is the main difference
307     * in the protocol:
308     * if (requestInProgress)
309     * {
310     *     wait(endRequestEvent);
311     * }
312     * requestInProgress = trans;
313     */
314
315    // Prepare the transaction.
316    auto *trans = packet2payload(packet);
317
318    /*
319     * Pay for annotated transport delays.
320     *
321     * The header delay marks the point in time, when the packet first is seen
322     * by the transactor. This is the point in time when the transactor needs
323     * to send the BEGIN_REQ to the SystemC world.
324     *
325     * NOTE: We drop the payload delay here. Normally, the receiver would be
326     *       responsible for handling the payload delay. In this case, however,
327     *       the receiver is a SystemC module and has no notion of the gem5
328     *       transport protocol and we cannot simply forward the
329     *       payload delay to the receiving module. Instead, we expect the
330     *       receiving SystemC module to model the payload delay by deferring
331     *       the END_REQ. This could lead to incorrect delays, if the XBar
332     *       payload delay is longer than the time the receiver needs to accept
333     *       the request (time between BEGIN_REQ and END_REQ).
334     *
335     * TODO: We could detect the case described above by remembering the
336     *       payload delay and comparing it to the time between BEGIN_REQ and
337     *       END_REQ. Then, a warning should be printed.
338     */
339    auto delay = sc_core::sc_time::from_value(packet->payloadDelay);
340    // Reset the delays
341    packet->payloadDelay = 0;
342    packet->headerDelay = 0;
343
344    // Starting TLM non-blocking sequence (AT) Refer to IEEE1666-2011 SystemC
345    // Standard Page 507 for a visualisation of the procedure.
346    tlm::tlm_phase phase = tlm::BEGIN_REQ;
347    tlm::tlm_sync_enum status;
348    status = socket->nb_transport_fw(*trans, phase, delay);
349    // Check returned value:
350    if (status == tlm::TLM_ACCEPTED) {
351        sc_assert(phase == tlm::BEGIN_REQ);
352        // Accepted but is now blocking until END_REQ (exclusion rule).
353        blockingRequest = trans;
354    } else if (status == tlm::TLM_UPDATED) {
355        // The Timing annotation must be honored:
356        sc_assert(phase == tlm::END_REQ || phase == tlm::BEGIN_RESP);
357
358        auto *pe = new Gem5SystemC::PayloadEvent<Gem5ToTlmBridge>(
359                *this, &Gem5ToTlmBridge::pec, "PEQ");
360        Tick nextEventTick = curTick() + delay.value();
361        system->wakeupEventQueue(nextEventTick);
362        system->schedule(pe, nextEventTick);
363    } else if (status == tlm::TLM_COMPLETED) {
364        // Transaction is over nothing has do be done.
365        sc_assert(phase == tlm::END_RESP);
366        trans->release();
367    }
368
369    return true;
370}
371
372template <unsigned int BITWIDTH>
373bool
374Gem5ToTlmBridge<BITWIDTH>::recvTimingSnoopResp(PacketPtr packet)
375{
376    // Snooping should be implemented with tlm_dbg_transport.
377    SC_REPORT_FATAL("Gem5ToTlmBridge",
378            "unimplemented func.: recvTimingSnoopResp");
379    return false;
380}
381
382template <unsigned int BITWIDTH>
383bool
384Gem5ToTlmBridge<BITWIDTH>::tryTiming(PacketPtr packet)
385{
386    panic("tryTiming(PacketPtr) isn't implemented.");
387}
388
389template <unsigned int BITWIDTH>
390void
391Gem5ToTlmBridge<BITWIDTH>::recvRespRetry()
392{
393    /* Retry a response */
394    sc_assert(blockingResponse);
395
396    tlm::tlm_generic_payload *trans = blockingResponse;
397    blockingResponse = nullptr;
398    PacketPtr packet =
399        Gem5SystemC::Gem5Extension::getExtension(trans).getPacket();
400
401    bool need_retry = !bsp.sendTimingResp(packet);
402
403    sc_assert(!need_retry);
404
405    sc_core::sc_time delay = sc_core::SC_ZERO_TIME;
406    tlm::tlm_phase phase = tlm::END_RESP;
407    socket->nb_transport_fw(*trans, phase, delay);
408    // Release transaction with all the extensions
409    trans->release();
410}
411
412// Similar to TLM's debug transport.
413template <unsigned int BITWIDTH>
414void
415Gem5ToTlmBridge<BITWIDTH>::recvFunctional(PacketPtr packet)
416{
417    // Prepare the transaction.
418    auto *trans = packet2payload(packet);
419
420    /* Execute Debug Transport: */
421    unsigned int bytes = socket->transport_dbg(*trans);
422    if (bytes != trans->get_data_length()) {
423        SC_REPORT_FATAL("Gem5ToTlmBridge",
424                "debug transport was not completed");
425    }
426
427    trans->release();
428}
429
430template <unsigned int BITWIDTH>
431tlm::tlm_sync_enum
432Gem5ToTlmBridge<BITWIDTH>::nb_transport_bw(tlm::tlm_generic_payload &trans,
433    tlm::tlm_phase &phase, sc_core::sc_time &delay)
434{
435    auto *pe = new Gem5SystemC::PayloadEvent<Gem5ToTlmBridge>(
436            *this, &Gem5ToTlmBridge::pec, "PE");
437    Tick nextEventTick = curTick() + delay.value();
438    system->wakeupEventQueue(nextEventTick);
439    system->schedule(pe, nextEventTick);
440    return tlm::TLM_ACCEPTED;
441}
442
443template <unsigned int BITWIDTH>
444void
445Gem5ToTlmBridge<BITWIDTH>::invalidate_direct_mem_ptr(
446        sc_dt::uint64 start_range, sc_dt::uint64 end_range)
447{
448    AddrRange r(start_range, end_range);
449
450    for (;;) {
451        auto it = backdoorMap.intersects(r);
452        if (it == backdoorMap.end())
453            break;
454
455        it->second->invalidate();
456        delete it->second;
457        backdoorMap.erase(it);
458    };
459}
460
461template <unsigned int BITWIDTH>
462Gem5ToTlmBridge<BITWIDTH>::Gem5ToTlmBridge(
463        Params *params, const sc_core::sc_module_name &mn) :
464    Gem5ToTlmBridgeBase(mn), bsp(std::string(name()) + ".gem5", *this),
465    socket("tlm_socket"),
466    wrapper(socket, std::string(name()) + ".tlm", InvalidPortID),
467    system(params->system), blockingRequest(nullptr),
468    needToSendRequestRetry(false), blockingResponse(nullptr),
469    addrRanges(params->addr_ranges.begin(), params->addr_ranges.end())
470{
471}
472
473template <unsigned int BITWIDTH>
474::Port &
475Gem5ToTlmBridge<BITWIDTH>::gem5_getPort(const std::string &if_name, int idx)
476{
477    if (if_name == "gem5")
478        return bsp;
479    else if (if_name == "tlm")
480        return wrapper;
481
482    return sc_core::sc_module::gem5_getPort(if_name, idx);
483}
484
485template <unsigned int BITWIDTH>
486void
487Gem5ToTlmBridge<BITWIDTH>::before_end_of_elaboration()
488{
489    bsp.sendRangeChange();
490
491    socket.register_nb_transport_bw(this, &Gem5ToTlmBridge::nb_transport_bw);
492    socket.register_invalidate_direct_mem_ptr(
493            this, &Gem5ToTlmBridge::invalidate_direct_mem_ptr);
494    sc_core::sc_module::before_end_of_elaboration();
495}
496
497} // namespace sc_gem5
498
499sc_gem5::Gem5ToTlmBridge<32> *
500Gem5ToTlmBridge32Params::create()
501{
502    return new sc_gem5::Gem5ToTlmBridge<32>(
503            this, sc_core::sc_module_name(name.c_str()));
504}
505
506sc_gem5::Gem5ToTlmBridge<64> *
507Gem5ToTlmBridge64Params::create()
508{
509    return new sc_gem5::Gem5ToTlmBridge<64>(
510            this, sc_core::sc_module_name(name.c_str()));
511}
512