tlm_to_gem5.cc revision 13823:040971e0f728
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
68namespace sc_gem5
69{
70
71template <unsigned int BITWIDTH>
72void
73TlmToGem5Bridge<BITWIDTH>::sendEndReq(tlm::tlm_generic_payload &trans)
74{
75    tlm::tlm_phase phase = tlm::END_REQ;
76    auto delay = sc_core::SC_ZERO_TIME;
77
78    auto status = socket->nb_transport_bw(trans, phase, delay);
79    panic_if(status != tlm::TLM_ACCEPTED,
80             "Unexpected status after sending END_REQ");
81}
82
83template <unsigned int BITWIDTH>
84void
85TlmToGem5Bridge<BITWIDTH>::sendBeginResp(tlm::tlm_generic_payload &trans,
86                                         sc_core::sc_time &delay)
87{
88    tlm::tlm_phase phase = tlm::BEGIN_RESP;
89
90    trans.set_response_status(tlm::TLM_OK_RESPONSE);
91
92    auto status = socket->nb_transport_bw(trans, phase, delay);
93
94    if (status == tlm::TLM_COMPLETED ||
95        (status == tlm::TLM_UPDATED && phase == tlm::END_RESP)) {
96        // transaction completed -> no need to wait for tlm::END_RESP
97        responseInProgress = false;
98    } else if (status == tlm::TLM_ACCEPTED) {
99        // we need to wait for tlm::END_RESP
100        responseInProgress = true;
101    } else {
102        panic("Unexpected status after sending BEGIN_RESP");
103    }
104}
105
106template <unsigned int BITWIDTH>
107void
108TlmToGem5Bridge<BITWIDTH>::handleBeginReq(tlm::tlm_generic_payload &trans)
109{
110    sc_assert(!waitForRetry);
111    sc_assert(pendingRequest == nullptr);
112    sc_assert(pendingPacket == nullptr);
113
114    trans.acquire();
115
116    PacketPtr pkt = nullptr;
117
118    Gem5SystemC::Gem5Extension *extension = nullptr;
119    trans.get_extension(extension);
120
121    // If there is an extension, this transaction was initiated by the gem5
122    // world and we can pipe through the original packet. Otherwise, we
123    // generate a new packet based on the transaction.
124    if (extension != nullptr) {
125        extension->setPipeThrough();
126        pkt = extension->getPacket();
127    } else {
128        pkt = generatePacket(trans);
129    }
130
131    auto tlmSenderState = new TlmSenderState(trans);
132    pkt->pushSenderState(tlmSenderState);
133
134    if (bmp.sendTimingReq(pkt)) { // port is free -> send END_REQ immediately
135        sendEndReq(trans);
136        trans.release();
137    } else { // port is blocked -> wait for retry before sending END_REQ
138        waitForRetry = true;
139        pendingRequest = &trans;
140        pendingPacket = pkt;
141    }
142}
143
144template <unsigned int BITWIDTH>
145void
146TlmToGem5Bridge<BITWIDTH>::handleEndResp(tlm::tlm_generic_payload &trans)
147{
148    sc_assert(responseInProgress);
149
150    responseInProgress = false;
151
152    checkTransaction(trans);
153
154    if (needToSendRetry) {
155        bmp.sendRetryResp();
156        needToSendRetry = false;
157    }
158}
159
160template <unsigned int BITWIDTH>
161PacketPtr
162TlmToGem5Bridge<BITWIDTH>::generatePacket(tlm::tlm_generic_payload &trans)
163{
164    MemCmd cmd;
165
166    switch (trans.get_command()) {
167        case tlm::TLM_READ_COMMAND:
168            cmd = MemCmd::ReadReq;
169            break;
170        case tlm::TLM_WRITE_COMMAND:
171            cmd = MemCmd::WriteReq;
172            break;
173        case tlm::TLM_IGNORE_COMMAND:
174            return nullptr;
175        default:
176            SC_REPORT_FATAL("TlmToGem5Bridge",
177                            "received transaction with unsupported command");
178    }
179
180    Request::Flags flags;
181    auto req = std::make_shared<Request>(
182        trans.get_address(), trans.get_data_length(), flags, masterId);
183
184    /*
185     * Allocate a new Packet. The packet will be deleted when it returns from
186     * the gem5 world as a response.
187     */
188    auto pkt = new Packet(req, cmd);
189    pkt->dataStatic(trans.get_data_ptr());
190
191    return pkt;
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>::peq_cb(tlm::tlm_generic_payload &trans,
216                                  const tlm::tlm_phase &phase)
217{
218    switch (phase) {
219        case tlm::BEGIN_REQ:
220            handleBeginReq(trans);
221            break;
222        case tlm::END_RESP:
223            handleEndResp(trans);
224            break;
225        default:
226            panic("unimplemented phase in callback");
227    }
228}
229
230template <unsigned int BITWIDTH>
231tlm::tlm_sync_enum
232TlmToGem5Bridge<BITWIDTH>::nb_transport_fw(
233        tlm::tlm_generic_payload &trans, tlm::tlm_phase &phase,
234        sc_core::sc_time &delay)
235{
236    unsigned len = trans.get_data_length();
237    unsigned char *byteEnable = trans.get_byte_enable_ptr();
238    unsigned width = trans.get_streaming_width();
239
240    // check the transaction attributes for unsupported features ...
241    if (byteEnable != 0) {
242        trans.set_response_status(tlm::TLM_BYTE_ENABLE_ERROR_RESPONSE);
243        return tlm::TLM_COMPLETED;
244    }
245    if (width < len) { // is this a burst request?
246        trans.set_response_status(tlm::TLM_BURST_ERROR_RESPONSE);
247        return tlm::TLM_COMPLETED;
248    }
249
250    // ... and queue the valid transaction
251    trans.acquire();
252    peq.notify(trans, phase, delay);
253    return tlm::TLM_ACCEPTED;
254}
255
256template <unsigned int BITWIDTH>
257void
258TlmToGem5Bridge<BITWIDTH>::b_transport(tlm::tlm_generic_payload &trans,
259                                       sc_core::sc_time &t)
260{
261    Gem5SystemC::Gem5Extension *extension = nullptr;
262    trans.get_extension(extension);
263
264    PacketPtr pkt = nullptr;
265
266    // If there is an extension, this transaction was initiated by the gem5
267    // world and we can pipe through the original packet.
268    if (extension != nullptr) {
269        extension->setPipeThrough();
270        pkt = extension->getPacket();
271    } else {
272        pkt = generatePacket(trans);
273    }
274
275    Tick ticks = bmp.sendAtomic(pkt);
276
277    // send an atomic request to gem5
278    panic_if(pkt->needsResponse() && !pkt->isResponse(),
279             "Packet sending failed!\n");
280
281    auto delay =
282      sc_core::sc_time((double)(ticks / SimClock::Int::ps), sc_core::SC_PS);
283
284    // update time
285    t += delay;
286
287    if (extension == nullptr)
288        destroyPacket(pkt);
289
290    trans.set_response_status(tlm::TLM_OK_RESPONSE);
291}
292
293template <unsigned int BITWIDTH>
294unsigned int
295TlmToGem5Bridge<BITWIDTH>::transport_dbg(tlm::tlm_generic_payload &trans)
296{
297    Gem5SystemC::Gem5Extension *extension = nullptr;
298    trans.get_extension(extension);
299
300    // If there is an extension, this transaction was initiated by the gem5
301    // world and we can pipe through the original packet.
302    if (extension != nullptr) {
303        extension->setPipeThrough();
304        bmp.sendFunctional(extension->getPacket());
305    } else {
306        auto pkt = generatePacket(trans);
307        if (pkt) {
308            bmp.sendFunctional(pkt);
309            destroyPacket(pkt);
310        }
311    }
312
313    return trans.get_data_length();
314}
315
316template <unsigned int BITWIDTH>
317bool
318TlmToGem5Bridge<BITWIDTH>::get_direct_mem_ptr(tlm::tlm_generic_payload &trans,
319                                              tlm::tlm_dmi &dmi_data)
320{
321    return false;
322}
323
324template <unsigned int BITWIDTH>
325bool
326TlmToGem5Bridge<BITWIDTH>::recvTimingResp(PacketPtr pkt)
327{
328    // exclusion rule
329    // We need to Wait for END_RESP before sending next BEGIN_RESP
330    if (responseInProgress) {
331        sc_assert(!needToSendRetry);
332        needToSendRetry = true;
333        return false;
334    }
335
336    sc_assert(pkt->isResponse());
337
338    /*
339     * Pay for annotated transport delays.
340     *
341     * See recvTimingReq in sc_slave_port.cc for a detailed description.
342     */
343    auto delay = sc_core::sc_time::from_value(pkt->payloadDelay);
344    // reset the delays
345    pkt->payloadDelay = 0;
346    pkt->headerDelay = 0;
347
348    auto tlmSenderState = dynamic_cast<TlmSenderState*>(pkt->popSenderState());
349    sc_assert(tlmSenderState != nullptr);
350
351    auto &trans = tlmSenderState->trans;
352
353    Gem5SystemC::Gem5Extension *extension = nullptr;
354    trans.get_extension(extension);
355
356    // clean up
357    delete tlmSenderState;
358
359    // If there is an extension the packet was piped through and we must not
360    // delete it. The packet travels back with the transaction.
361    if (extension == nullptr)
362        destroyPacket(pkt);
363    else
364        sc_assert(extension->isPipeThrough());
365
366    sendBeginResp(trans, delay);
367    trans.release();
368
369    return true;
370}
371
372template <unsigned int BITWIDTH>
373void
374TlmToGem5Bridge<BITWIDTH>::recvReqRetry()
375{
376    sc_assert(waitForRetry);
377    sc_assert(pendingRequest != nullptr);
378    sc_assert(pendingPacket != nullptr);
379
380    if (bmp.sendTimingReq(pendingPacket)) {
381        waitForRetry = false;
382        pendingPacket = nullptr;
383
384        auto &trans = *pendingRequest;
385        sendEndReq(trans);
386        trans.release();
387
388        pendingRequest = nullptr;
389    }
390}
391
392template <unsigned int BITWIDTH>
393void
394TlmToGem5Bridge<BITWIDTH>::recvRangeChange()
395{
396    SC_REPORT_WARNING("TlmToGem5Bridge",
397                      "received address range change but ignored it");
398}
399
400template <unsigned int BITWIDTH>
401::Port &
402TlmToGem5Bridge<BITWIDTH>::gem5_getPort(const std::string &if_name, int idx)
403{
404    if (if_name == "gem5")
405        return bmp;
406    else if (if_name == "tlm")
407        return wrapper;
408
409    return sc_core::sc_module::gem5_getPort(if_name, idx);
410}
411
412template <unsigned int BITWIDTH>
413TlmToGem5Bridge<BITWIDTH>::TlmToGem5Bridge(
414        Params *params, const sc_core::sc_module_name &mn) :
415    TlmToGem5BridgeBase(mn), peq(this, &TlmToGem5Bridge<BITWIDTH>::peq_cb),
416    waitForRetry(false), pendingRequest(nullptr), pendingPacket(nullptr),
417    needToSendRetry(false), responseInProgress(false),
418    bmp(std::string(name()) + "master", *this), socket("tlm_socket"),
419    wrapper(socket, std::string(name()) + ".tlm", InvalidPortID),
420    system(params->system),
421    masterId(params->system->getGlobalMasterId(
422                std::string("[systemc].") + name()))
423{
424}
425
426template <unsigned int BITWIDTH>
427void
428TlmToGem5Bridge<BITWIDTH>::before_end_of_elaboration()
429{
430    /*
431     * Register the TLM non-blocking interface when using gem5 Timing mode and
432     * the TLM blocking interface when using the gem5 Atomic mode.
433     * Then the magic (TM) in simple_target_socket automatically transforms
434     * non-blocking in blocking transactions and vice versa.
435     *
436     * NOTE: The mode may change during execution.
437     */
438    if (system->isTimingMode()) {
439        SC_REPORT_INFO("TlmToGem5Bridge", "register non-blocking interface");
440        socket.register_nb_transport_fw(
441                this, &TlmToGem5Bridge<BITWIDTH>::nb_transport_fw);
442    } else if (system->isAtomicMode()) {
443        SC_REPORT_INFO("TlmToGem5Bridge", "register blocking interface");
444        socket.register_b_transport(
445                this, &TlmToGem5Bridge<BITWIDTH>::b_transport);
446    } else {
447        panic("gem5 operates neither in Timing nor in Atomic mode");
448    }
449
450    socket.register_transport_dbg(
451            this, &TlmToGem5Bridge<BITWIDTH>::transport_dbg);
452
453    sc_core::sc_module::before_end_of_elaboration();
454}
455
456} // namespace sc_gem5
457
458sc_gem5::TlmToGem5Bridge<32> *
459TlmToGem5Bridge32Params::create()
460{
461    return new sc_gem5::TlmToGem5Bridge<32>(
462            this, sc_core::sc_module_name(name.c_str()));
463}
464
465sc_gem5::TlmToGem5Bridge<64> *
466TlmToGem5Bridge64Params::create()
467{
468    return new sc_gem5::TlmToGem5Bridge<64>(
469            this, sc_core::sc_module_name(name.c_str()));
470}
471