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) 2016, Dresden University of Technology (TU Dresden)
28 * All rights reserved.
29 *
30 * Redistribution and use in source and binary forms, with or without
31 * modification, are permitted provided that the following conditions are
32 * met:
33 *
34 * 1. Redistributions of source code must retain the above copyright notice,
35 *    this list of conditions and the following disclaimer.
36 *
37 * 2. Redistributions in binary form must reproduce the above copyright
38 *    notice, this list of conditions and the following disclaimer in the
39 *    documentation and/or other materials provided with the distribution.
40 *
41 * 3. Neither the name of the copyright holder nor the names of its
42 *    contributors may be used to endorse or promote products derived from
43 *    this software without specific prior written permission.
44 *
45 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
46 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
47 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
48 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
49 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
50 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
51 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
52 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
53 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
54 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
55 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
56 *
57 * Authors: Gabe Black
58 *          Christian Menard
59 */
60
61#include "systemc/tlm_bridge/tlm_to_gem5.hh"
62
63#include "params/TlmToGem5Bridge32.hh"
64#include "params/TlmToGem5Bridge64.hh"
65#include "sim/system.hh"
66#include "systemc/ext/core/sc_module_name.hh"
67#include "systemc/ext/core/sc_time.hh"
68
69namespace sc_gem5
70{
71
72PacketPtr
73payload2packet(MasterID masterId, tlm::tlm_generic_payload &trans)
74{
75    MemCmd cmd;
76
77    switch (trans.get_command()) {
78        case tlm::TLM_READ_COMMAND:
79            cmd = MemCmd::ReadReq;
80            break;
81        case tlm::TLM_WRITE_COMMAND:
82            cmd = MemCmd::WriteReq;
83            break;
84        case tlm::TLM_IGNORE_COMMAND:
85            return nullptr;
86        default:
87            SC_REPORT_FATAL("TlmToGem5Bridge",
88                            "received transaction with unsupported command");
89    }
90
91    Request::Flags flags;
92    auto req = std::make_shared<Request>(
93        trans.get_address(), trans.get_data_length(), flags, masterId);
94
95    /*
96     * Allocate a new Packet. The packet will be deleted when it returns from
97     * the gem5 world as a response.
98     */
99    auto pkt = new Packet(req, cmd);
100    pkt->dataStatic(trans.get_data_ptr());
101
102    return pkt;
103}
104
105template <unsigned int BITWIDTH>
106void
107TlmToGem5Bridge<BITWIDTH>::sendEndReq(tlm::tlm_generic_payload &trans)
108{
109    tlm::tlm_phase phase = tlm::END_REQ;
110    auto delay = sc_core::SC_ZERO_TIME;
111
112    auto status = socket->nb_transport_bw(trans, phase, delay);
113    panic_if(status != tlm::TLM_ACCEPTED,
114             "Unexpected status after sending END_REQ");
115}
116
117template <unsigned int BITWIDTH>
118void
119TlmToGem5Bridge<BITWIDTH>::sendBeginResp(tlm::tlm_generic_payload &trans,
120                                         sc_core::sc_time &delay)
121{
122    tlm::tlm_phase phase = tlm::BEGIN_RESP;
123
124    trans.set_response_status(tlm::TLM_OK_RESPONSE);
125
126    auto status = socket->nb_transport_bw(trans, phase, delay);
127
128    if (status == tlm::TLM_COMPLETED ||
129        (status == tlm::TLM_UPDATED && phase == tlm::END_RESP)) {
130        // transaction completed -> no need to wait for tlm::END_RESP
131        responseInProgress = false;
132    } else if (status == tlm::TLM_ACCEPTED) {
133        // we need to wait for tlm::END_RESP
134        responseInProgress = true;
135    } else {
136        panic("Unexpected status after sending BEGIN_RESP");
137    }
138}
139
140template <unsigned int BITWIDTH>
141void
142TlmToGem5Bridge<BITWIDTH>::handleBeginReq(tlm::tlm_generic_payload &trans)
143{
144    sc_assert(!waitForRetry);
145    sc_assert(pendingRequest == nullptr);
146    sc_assert(pendingPacket == nullptr);
147
148    trans.acquire();
149
150    PacketPtr pkt = nullptr;
151
152    Gem5SystemC::Gem5Extension *extension = nullptr;
153    trans.get_extension(extension);
154
155    // If there is an extension, this transaction was initiated by the gem5
156    // world and we can pipe through the original packet. Otherwise, we
157    // generate a new packet based on the transaction.
158    if (extension != nullptr) {
159        extension->setPipeThrough();
160        pkt = extension->getPacket();
161    } else {
162        pkt = payload2packet(masterId, trans);
163    }
164
165    auto tlmSenderState = new TlmSenderState(trans);
166    pkt->pushSenderState(tlmSenderState);
167
168    if (bmp.sendTimingReq(pkt)) { // port is free -> send END_REQ immediately
169        sendEndReq(trans);
170        trans.release();
171    } else { // port is blocked -> wait for retry before sending END_REQ
172        waitForRetry = true;
173        pendingRequest = &trans;
174        pendingPacket = pkt;
175    }
176}
177
178template <unsigned int BITWIDTH>
179void
180TlmToGem5Bridge<BITWIDTH>::handleEndResp(tlm::tlm_generic_payload &trans)
181{
182    sc_assert(responseInProgress);
183
184    responseInProgress = false;
185
186    checkTransaction(trans);
187
188    if (needToSendRetry) {
189        bmp.sendRetryResp();
190        needToSendRetry = false;
191    }
192}
193
194template <unsigned int BITWIDTH>
195void
196TlmToGem5Bridge<BITWIDTH>::destroyPacket(PacketPtr pkt)
197{
198    delete pkt;
199}
200
201template <unsigned int BITWIDTH>
202void
203TlmToGem5Bridge<BITWIDTH>::checkTransaction(tlm::tlm_generic_payload &trans)
204{
205    if (trans.is_response_error()) {
206        std::stringstream ss;
207        ss << "Transaction returned with error, response status = "
208           << trans.get_response_string();
209        SC_REPORT_ERROR("TLM-2", ss.str().c_str());
210    }
211}
212
213template <unsigned int BITWIDTH>
214void
215TlmToGem5Bridge<BITWIDTH>::invalidateDmi(const ::MemBackdoor &backdoor)
216{
217    socket->invalidate_direct_mem_ptr(
218            backdoor.range().start(), backdoor.range().end());
219}
220
221template <unsigned int BITWIDTH>
222void
223TlmToGem5Bridge<BITWIDTH>::peq_cb(tlm::tlm_generic_payload &trans,
224                                  const tlm::tlm_phase &phase)
225{
226    switch (phase) {
227        case tlm::BEGIN_REQ:
228            handleBeginReq(trans);
229            break;
230        case tlm::END_RESP:
231            handleEndResp(trans);
232            break;
233        default:
234            panic("unimplemented phase in callback");
235    }
236}
237
238template <unsigned int BITWIDTH>
239tlm::tlm_sync_enum
240TlmToGem5Bridge<BITWIDTH>::nb_transport_fw(
241        tlm::tlm_generic_payload &trans, tlm::tlm_phase &phase,
242        sc_core::sc_time &delay)
243{
244    unsigned len = trans.get_data_length();
245    unsigned char *byteEnable = trans.get_byte_enable_ptr();
246    unsigned width = trans.get_streaming_width();
247
248    // check the transaction attributes for unsupported features ...
249    if (byteEnable != 0) {
250        trans.set_response_status(tlm::TLM_BYTE_ENABLE_ERROR_RESPONSE);
251        return tlm::TLM_COMPLETED;
252    }
253    if (width < len) { // is this a burst request?
254        trans.set_response_status(tlm::TLM_BURST_ERROR_RESPONSE);
255        return tlm::TLM_COMPLETED;
256    }
257
258    // ... and queue the valid transaction
259    trans.acquire();
260    peq.notify(trans, phase, delay);
261    return tlm::TLM_ACCEPTED;
262}
263
264template <unsigned int BITWIDTH>
265void
266TlmToGem5Bridge<BITWIDTH>::b_transport(tlm::tlm_generic_payload &trans,
267                                       sc_core::sc_time &t)
268{
269    Gem5SystemC::Gem5Extension *extension = nullptr;
270    trans.get_extension(extension);
271
272    PacketPtr pkt = nullptr;
273
274    // If there is an extension, this transaction was initiated by the gem5
275    // world and we can pipe through the original packet.
276    if (extension != nullptr) {
277        extension->setPipeThrough();
278        pkt = extension->getPacket();
279    } else {
280        pkt = payload2packet(masterId, trans);
281    }
282
283    MemBackdoorPtr backdoor = nullptr;
284    Tick ticks = bmp.sendAtomicBackdoor(pkt, backdoor);
285    if (backdoor)
286        trans.set_dmi_allowed(true);
287
288    // send an atomic request to gem5
289    panic_if(pkt->needsResponse() && !pkt->isResponse(),
290             "Packet sending failed!\n");
291
292    auto delay =
293      sc_core::sc_time((double)(ticks / SimClock::Int::ps), sc_core::SC_PS);
294
295    // update time
296    t += delay;
297
298    if (extension == nullptr)
299        destroyPacket(pkt);
300
301    trans.set_response_status(tlm::TLM_OK_RESPONSE);
302}
303
304template <unsigned int BITWIDTH>
305unsigned int
306TlmToGem5Bridge<BITWIDTH>::transport_dbg(tlm::tlm_generic_payload &trans)
307{
308    Gem5SystemC::Gem5Extension *extension = nullptr;
309    trans.get_extension(extension);
310
311    // If there is an extension, this transaction was initiated by the gem5
312    // world and we can pipe through the original packet.
313    if (extension != nullptr) {
314        extension->setPipeThrough();
315        bmp.sendFunctional(extension->getPacket());
316    } else {
317        auto pkt = payload2packet(masterId, trans);
318        if (pkt) {
319            bmp.sendFunctional(pkt);
320            destroyPacket(pkt);
321        }
322    }
323
324    return trans.get_data_length();
325}
326
327template <unsigned int BITWIDTH>
328bool
329TlmToGem5Bridge<BITWIDTH>::get_direct_mem_ptr(tlm::tlm_generic_payload &trans,
330                                              tlm::tlm_dmi &dmi_data)
331{
332    Gem5SystemC::Gem5Extension *extension = nullptr;
333    trans.get_extension(extension);
334
335    PacketPtr pkt = nullptr;
336
337    // If there is an extension, this transaction was initiated by the gem5
338    // world and we can pipe through the original packet.
339    if (extension != nullptr) {
340        extension->setPipeThrough();
341        pkt = extension->getPacket();
342    } else {
343        pkt = payload2packet(masterId, trans);
344        pkt->req->setFlags(Request::NO_ACCESS);
345    }
346
347    MemBackdoorPtr backdoor = nullptr;
348    bmp.sendAtomicBackdoor(pkt, backdoor);
349    if (backdoor) {
350        trans.set_dmi_allowed(true);
351        dmi_data.set_dmi_ptr(backdoor->ptr());
352        dmi_data.set_start_address(backdoor->range().start());
353        dmi_data.set_end_address(backdoor->range().end());
354
355        typedef tlm::tlm_dmi::dmi_access_e access_t;
356        access_t access = tlm::tlm_dmi::DMI_ACCESS_NONE;
357        if (backdoor->readable())
358            access = (access_t)(access | tlm::tlm_dmi::DMI_ACCESS_READ);
359        if (backdoor->writeable())
360            access = (access_t)(access | tlm::tlm_dmi::DMI_ACCESS_WRITE);
361        dmi_data.set_granted_access(access);
362
363        backdoor->addInvalidationCallback(
364            [this](const MemBackdoor &backdoor)
365            {
366                invalidateDmi(backdoor);
367            }
368        );
369    }
370
371    if (extension == nullptr)
372        destroyPacket(pkt);
373
374    trans.set_response_status(tlm::TLM_OK_RESPONSE);
375
376    return backdoor != nullptr;
377}
378
379template <unsigned int BITWIDTH>
380bool
381TlmToGem5Bridge<BITWIDTH>::recvTimingResp(PacketPtr pkt)
382{
383    // exclusion rule
384    // We need to Wait for END_RESP before sending next BEGIN_RESP
385    if (responseInProgress) {
386        sc_assert(!needToSendRetry);
387        needToSendRetry = true;
388        return false;
389    }
390
391    sc_assert(pkt->isResponse());
392
393    /*
394     * Pay for annotated transport delays.
395     *
396     * See recvTimingReq in sc_slave_port.cc for a detailed description.
397     */
398    auto delay = sc_core::sc_time::from_value(pkt->payloadDelay);
399    // reset the delays
400    pkt->payloadDelay = 0;
401    pkt->headerDelay = 0;
402
403    auto tlmSenderState = dynamic_cast<TlmSenderState*>(pkt->popSenderState());
404    sc_assert(tlmSenderState != nullptr);
405
406    auto &trans = tlmSenderState->trans;
407
408    Gem5SystemC::Gem5Extension *extension = nullptr;
409    trans.get_extension(extension);
410
411    // clean up
412    delete tlmSenderState;
413
414    // If there is an extension the packet was piped through and we must not
415    // delete it. The packet travels back with the transaction.
416    if (extension == nullptr)
417        destroyPacket(pkt);
418    else
419        sc_assert(extension->isPipeThrough());
420
421    sendBeginResp(trans, delay);
422    trans.release();
423
424    return true;
425}
426
427template <unsigned int BITWIDTH>
428void
429TlmToGem5Bridge<BITWIDTH>::recvReqRetry()
430{
431    sc_assert(waitForRetry);
432    sc_assert(pendingRequest != nullptr);
433    sc_assert(pendingPacket != nullptr);
434
435    if (bmp.sendTimingReq(pendingPacket)) {
436        waitForRetry = false;
437        pendingPacket = nullptr;
438
439        auto &trans = *pendingRequest;
440        sendEndReq(trans);
441        trans.release();
442
443        pendingRequest = nullptr;
444    }
445}
446
447template <unsigned int BITWIDTH>
448void
449TlmToGem5Bridge<BITWIDTH>::recvRangeChange()
450{
451    SC_REPORT_WARNING("TlmToGem5Bridge",
452                      "received address range change but ignored it");
453}
454
455template <unsigned int BITWIDTH>
456::Port &
457TlmToGem5Bridge<BITWIDTH>::gem5_getPort(const std::string &if_name, int idx)
458{
459    if (if_name == "gem5")
460        return bmp;
461    else if (if_name == "tlm")
462        return wrapper;
463
464    return sc_core::sc_module::gem5_getPort(if_name, idx);
465}
466
467template <unsigned int BITWIDTH>
468TlmToGem5Bridge<BITWIDTH>::TlmToGem5Bridge(
469        Params *params, const sc_core::sc_module_name &mn) :
470    TlmToGem5BridgeBase(mn), peq(this, &TlmToGem5Bridge<BITWIDTH>::peq_cb),
471    waitForRetry(false), pendingRequest(nullptr), pendingPacket(nullptr),
472    needToSendRetry(false), responseInProgress(false),
473    bmp(std::string(name()) + "master", *this), socket("tlm_socket"),
474    wrapper(socket, std::string(name()) + ".tlm", InvalidPortID),
475    system(params->system),
476    masterId(params->system->getGlobalMasterId(
477                std::string("[systemc].") + name()))
478{
479}
480
481template <unsigned int BITWIDTH>
482void
483TlmToGem5Bridge<BITWIDTH>::before_end_of_elaboration()
484{
485    /*
486     * Register the TLM non-blocking interface when using gem5 Timing mode and
487     * the TLM blocking interface when using the gem5 Atomic mode.
488     * Then the magic (TM) in simple_target_socket automatically transforms
489     * non-blocking in blocking transactions and vice versa.
490     *
491     * NOTE: The mode may change during execution.
492     */
493    if (system->isTimingMode()) {
494        SC_REPORT_INFO("TlmToGem5Bridge", "register non-blocking interface");
495        socket.register_nb_transport_fw(
496                this, &TlmToGem5Bridge<BITWIDTH>::nb_transport_fw);
497    } else if (system->isAtomicMode()) {
498        SC_REPORT_INFO("TlmToGem5Bridge", "register blocking interface");
499        socket.register_b_transport(
500                this, &TlmToGem5Bridge<BITWIDTH>::b_transport);
501        socket.register_get_direct_mem_ptr(
502                this, &TlmToGem5Bridge<BITWIDTH>::get_direct_mem_ptr);
503    } else {
504        panic("gem5 operates neither in Timing nor in Atomic mode");
505    }
506
507    socket.register_transport_dbg(
508            this, &TlmToGem5Bridge<BITWIDTH>::transport_dbg);
509
510    sc_core::sc_module::before_end_of_elaboration();
511}
512
513} // namespace sc_gem5
514
515sc_gem5::TlmToGem5Bridge<32> *
516TlmToGem5Bridge32Params::create()
517{
518    return new sc_gem5::TlmToGem5Bridge<32>(
519            this, sc_core::sc_module_name(name.c_str()));
520}
521
522sc_gem5::TlmToGem5Bridge<64> *
523TlmToGem5Bridge64Params::create()
524{
525    return new sc_gem5::TlmToGem5Bridge<64>(
526            this, sc_core::sc_module_name(name.c_str()));
527}
528