2 * Copyright (c) 2015, University of Kaiserslautern
3 * Copyright (c) 2016, Dresden University of Technology (TU Dresden)
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
10 * 1. Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the copyright holder nor the names of its
18 * contributors may be used to endorse or promote products derived from
19 * this software without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
25 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 * Authors: Matthias Jung
40 #include "sc_slave_port.hh"
41 #include "slave_transactor.hh"
47 * Instantiate a tlm memory manager that takes care about all the
48 * tlm transactions in the system
53 * Convert a gem5 packet to a TLM payload by copying all the relevant
54 * information to a previously allocated tlm payload
57 packet2payload(PacketPtr packet
, tlm::tlm_generic_payload
&trans
)
59 trans
.set_address(packet
->getAddr());
61 /* Check if this transaction was allocated by mm */
62 sc_assert(trans
.has_mm());
64 unsigned int size
= packet
->getSize();
65 unsigned char *data
= packet
->getPtr
<unsigned char>();
67 trans
.set_data_length(size
);
68 trans
.set_streaming_width(size
);
69 trans
.set_data_ptr(data
);
71 if (packet
->isRead()) {
72 trans
.set_command(tlm::TLM_READ_COMMAND
);
74 else if (packet
->isInvalidate()) {
76 } else if (packet
->isWrite()) {
77 trans
.set_command(tlm::TLM_WRITE_COMMAND
);
79 SC_REPORT_FATAL("SCSlavePort", "No R/W packet");
84 * Similar to TLM's blocking transport (LT)
87 SCSlavePort::recvAtomic(PacketPtr packet
)
90 SC_REPORT_INFO("SCSlavePort", "recvAtomic hasn't been tested much");
92 panic_if(packet
->cacheResponding(), "Should not see packets where cache "
95 panic_if(!(packet
->isRead() || packet
->isWrite()),
96 "Should only see read and writes at TLM memory\n");
99 sc_core::sc_time delay
= sc_core::SC_ZERO_TIME
;
102 /* Prepare the transaction */
103 tlm::tlm_generic_payload
* trans
= mm
.allocate();
105 packet2payload(packet
, *trans
);
107 /* Attach the packet pointer to the TLM transaction to keep track */
108 Gem5Extension
* extension
= new Gem5Extension(packet
);
109 trans
->set_auto_extension(extension
);
111 /* Execute b_transport: */
112 if (packet
->cmd
== MemCmd::SwapReq
) {
113 SC_REPORT_FATAL("SCSlavePort", "SwapReq not supported");
114 } else if (packet
->isRead()) {
115 transactor
->socket
->b_transport(*trans
, delay
);
116 } else if (packet
->isInvalidate()) {
118 } else if (packet
->isWrite()) {
119 transactor
->socket
->b_transport(*trans
, delay
);
121 SC_REPORT_FATAL("SCSlavePort", "Typo of request not supported");
124 if (packet
->needsResponse()) {
125 packet
->makeResponse();
130 return delay
.value();
134 * Similar to TLM's debug transport
137 SCSlavePort::recvFunctional(PacketPtr packet
)
139 /* Prepare the transaction */
140 tlm::tlm_generic_payload
* trans
= mm
.allocate();
142 packet2payload(packet
, *trans
);
144 /* Attach the packet pointer to the TLM transaction to keep track */
145 Gem5Extension
* extension
= new Gem5Extension(packet
);
146 trans
->set_auto_extension(extension
);
148 /* Execute Debug Transport: */
149 unsigned int bytes
= transactor
->socket
->transport_dbg(*trans
);
150 if (bytes
!= trans
->get_data_length()) {
151 SC_REPORT_FATAL("SCSlavePort","debug transport was not completed");
158 SCSlavePort::recvTimingSnoopResp(PacketPtr packet
)
160 /* Snooping should be implemented with tlm_dbg_transport */
161 SC_REPORT_FATAL("SCSlavePort","unimplemented func.: recvTimingSnoopResp");
166 SCSlavePort::recvFunctionalSnoop(PacketPtr packet
)
168 /* Snooping should be implemented with tlm_dbg_transport */
169 SC_REPORT_FATAL("SCSlavePort","unimplemented func.: recvFunctionalSnoop");
173 * Similar to TLM's non-blocking transport (AT)
176 SCSlavePort::recvTimingReq(PacketPtr packet
)
180 panic_if(packet
->cacheResponding(), "Should not see packets where cache "
183 panic_if(!(packet
->isRead() || packet
->isWrite()),
184 "Should only see read and writes at TLM memory\n");
187 /* We should never get a second request after noting that a retry is
189 sc_assert(!needToSendRequestRetry
);
191 /* Remember if a request comes in while we're blocked so that a retry
192 * can be sent to gem5 */
193 if (blockingRequest
) {
194 needToSendRequestRetry
= true;
198 /* NOTE: normal tlm is blocking here. But in our case we return false
199 * and tell gem5 when a retry can be done. This is the main difference
201 * if (requestInProgress)
203 * wait(endRequestEvent);
205 * requestInProgress = trans;
208 /* Prepare the transaction */
209 tlm::tlm_generic_payload
* trans
= mm
.allocate();
211 packet2payload(packet
, *trans
);
213 /* Attach the packet pointer to the TLM transaction to keep track */
214 Gem5Extension
* extension
= new Gem5Extension(packet
);
215 trans
->set_auto_extension(extension
);
218 * Pay for annotated transport delays.
220 * The header delay marks the point in time, when the packet first is seen
221 * by the transactor. This is the point int time, when the transactor needs
222 * to send the BEGIN_REQ to the SystemC world.
224 * NOTE: We drop the payload delay here. Normally, the receiver would be
225 * responsible for handling the payload delay. In this case, however,
226 * the receiver is a SystemC module and has no notion of the gem5
227 * transport protocol and we cannot simply forward the
228 * payload delay to the receiving module. Instead, we expect the
229 * receiving SystemC module to model the payload delay by deferring
230 * the END_REQ. This could lead to incorrect delays, if the XBar
231 * payload delay is longer than the time the receiver needs to accept
232 * the request (time between BEGIN_REQ and END_REQ).
234 * TODO: We could detect the case described above by remembering the
235 * payload delay and comparing it to the time between BEGIN_REQ and
236 * END_REQ. Then, a warning should be printed.
238 auto delay
= sc_core::sc_time::from_value(packet
->payloadDelay
);
240 packet
->payloadDelay
= 0;
241 packet
->headerDelay
= 0;
243 /* Starting TLM non-blocking sequence (AT) Refer to IEEE1666-2011 SystemC
244 * Standard Page 507 for a visualisation of the procedure */
245 tlm::tlm_phase phase
= tlm::BEGIN_REQ
;
246 tlm::tlm_sync_enum status
;
247 status
= transactor
->socket
->nb_transport_fw(*trans
, phase
, delay
);
248 /* Check returned value: */
249 if (status
== tlm::TLM_ACCEPTED
) {
250 sc_assert(phase
== tlm::BEGIN_REQ
);
251 /* Accepted but is now blocking until END_REQ (exclusion rule)*/
252 blockingRequest
= trans
;
253 } else if (status
== tlm::TLM_UPDATED
) {
254 /* The Timing annotation must be honored: */
255 sc_assert(phase
== tlm::END_REQ
|| phase
== tlm::BEGIN_RESP
);
257 PayloadEvent
<SCSlavePort
> * pe
;
258 pe
= new PayloadEvent
<SCSlavePort
>(*this,
259 &SCSlavePort::pec
, "PEQ");
260 pe
->notify(*trans
, phase
, delay
);
261 } else if (status
== tlm::TLM_COMPLETED
) {
262 /* Transaction is over nothing has do be done. */
263 sc_assert(phase
== tlm::END_RESP
);
272 PayloadEvent
<SCSlavePort
> * pe
,
273 tlm::tlm_generic_payload
& trans
,
274 const tlm::tlm_phase
& phase
)
278 if (phase
== tlm::END_REQ
||
279 &trans
== blockingRequest
&& phase
== tlm::BEGIN_RESP
) {
280 sc_assert(&trans
== blockingRequest
);
281 blockingRequest
= NULL
;
283 /* Did another request arrive while blocked, schedule a retry */
284 if (needToSendRequestRetry
) {
285 needToSendRequestRetry
= false;
289 if (phase
== tlm::BEGIN_RESP
)
293 auto& extension
= Gem5Extension::getExtension(trans
);
294 auto packet
= extension
.getPacket();
296 sc_assert(!blockingResponse
);
298 bool need_retry
= false;
301 * If the packet was piped through and needs a response, we don't need
302 * to touch the packet and can forward it directly as a response.
303 * Otherwise, we need to make a response and send the transformed
306 if (extension
.isPipeThrough()) {
307 if (packet
->isResponse()) {
308 need_retry
= !sendTimingResp(packet
);
310 } else if (packet
->needsResponse()) {
311 packet
->makeResponse();
312 need_retry
= !sendTimingResp(packet
);
316 blockingResponse
= &trans
;
318 if (phase
== tlm::BEGIN_RESP
) {
319 /* Send END_RESP and we're finished: */
320 tlm::tlm_phase fw_phase
= tlm::END_RESP
;
321 sc_time delay
= SC_ZERO_TIME
;
322 transactor
->socket
->nb_transport_fw(trans
, fw_phase
, delay
);
323 /* Release the transaction with all the extensions */
332 SCSlavePort::recvRespRetry()
336 /* Retry a response */
337 sc_assert(blockingResponse
);
339 tlm::tlm_generic_payload
*trans
= blockingResponse
;
340 blockingResponse
= NULL
;
341 PacketPtr packet
= Gem5Extension::getExtension(trans
).getPacket();
343 bool need_retry
= !sendTimingResp(packet
);
345 sc_assert(!need_retry
);
347 sc_core::sc_time delay
= sc_core::SC_ZERO_TIME
;
348 tlm::tlm_phase phase
= tlm::END_RESP
;
349 transactor
->socket
->nb_transport_fw(*trans
, phase
, delay
);
350 // Release transaction with all the extensions
355 SCSlavePort::nb_transport_bw(tlm::tlm_generic_payload
& trans
,
356 tlm::tlm_phase
& phase
,
357 sc_core::sc_time
& delay
)
359 PayloadEvent
<SCSlavePort
> * pe
;
360 pe
= new PayloadEvent
<SCSlavePort
>(*this, &SCSlavePort::pec
, "PE");
361 pe
->notify(trans
, phase
, delay
);
362 return tlm::TLM_ACCEPTED
;
365 SCSlavePort::SCSlavePort(const std::string
&name_
,
366 const std::string
&systemc_name
,
367 ExternalSlave
&owner_
) :
368 ExternalSlave::Port(name_
, owner_
),
369 blockingRequest(NULL
),
370 needToSendRequestRetry(false),
371 blockingResponse(NULL
),
377 SCSlavePort::bindToTransactor(Gem5SlaveTransactor
* transactor
)
379 sc_assert(this->transactor
== nullptr);
381 this->transactor
= transactor
;
383 transactor
->socket
.register_nb_transport_bw(this,
384 &SCSlavePort::nb_transport_bw
);
388 SCSlavePortHandler::getExternalPort(const std::string
&name
,
389 ExternalSlave
&owner
,
390 const std::string
&port_data
)
392 // Create and register a new SystemC slave port
393 auto* port
= new SCSlavePort(name
, port_data
, owner
);
395 control
.registerSlavePort(port_data
, port
);