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