dcf7c5d9534b04bd52a75ddbbec39554eea26ea0
[gem5.git] / util / tlm / sc_slave_port.cc
1 /*
2 * Copyright (c) 2015, University of Kaiserslautern
3 * Copyright (c) 2016, Dresden University of Technology (TU Dresden)
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 *
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.
16 *
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.
20 *
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.
32 *
33 * Authors: Matthias Jung
34 * Abdul Mutaal Ahmad
35 * Christian Menard
36 */
37
38 #include "sc_ext.hh"
39 #include "sc_mm.hh"
40 #include "sc_slave_port.hh"
41 #include "slave_transactor.hh"
42
43 namespace Gem5SystemC
44 {
45
46 /**
47 * Instantiate a tlm memory manager that takes care about all the
48 * tlm transactions in the system
49 */
50 MemoryManager mm;
51
52 /**
53 * Convert a gem5 packet to a TLM payload by copying all the relevant
54 * information to a previously allocated tlm payload
55 */
56 void
57 packet2payload(PacketPtr packet, tlm::tlm_generic_payload &trans)
58 {
59 trans.set_address(packet->getAddr());
60
61 /* Check if this transaction was allocated by mm */
62 sc_assert(trans.has_mm());
63
64 unsigned int size = packet->getSize();
65 unsigned char *data = packet->getPtr<unsigned char>();
66
67 trans.set_data_length(size);
68 trans.set_streaming_width(size);
69 trans.set_data_ptr(data);
70
71 if (packet->isRead()) {
72 trans.set_command(tlm::TLM_READ_COMMAND);
73 }
74 else if (packet->isInvalidate()) {
75 /* Do nothing */
76 } else if (packet->isWrite()) {
77 trans.set_command(tlm::TLM_WRITE_COMMAND);
78 } else {
79 SC_REPORT_FATAL("SCSlavePort", "No R/W packet");
80 }
81 }
82
83 /**
84 * Similar to TLM's blocking transport (LT)
85 */
86 Tick
87 SCSlavePort::recvAtomic(PacketPtr packet)
88 {
89 CAUGHT_UP;
90 SC_REPORT_INFO("SCSlavePort", "recvAtomic hasn't been tested much");
91
92 panic_if(packet->cacheResponding(), "Should not see packets where cache "
93 "is responding");
94
95 panic_if(!(packet->isRead() || packet->isWrite()),
96 "Should only see read and writes at TLM memory\n");
97
98
99 sc_core::sc_time delay = sc_core::SC_ZERO_TIME;
100
101
102 /* Prepare the transaction */
103 tlm::tlm_generic_payload * trans = mm.allocate();
104 trans->acquire();
105 packet2payload(packet, *trans);
106
107 /* Attach the packet pointer to the TLM transaction to keep track */
108 Gem5Extension* extension = new Gem5Extension(packet);
109 trans->set_auto_extension(extension);
110
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()) {
117 // do nothing
118 } else if (packet->isWrite()) {
119 transactor->socket->b_transport(*trans, delay);
120 } else {
121 SC_REPORT_FATAL("SCSlavePort", "Typo of request not supported");
122 }
123
124 if (packet->needsResponse()) {
125 packet->makeResponse();
126 }
127
128 trans->release();
129
130 return delay.value();
131 }
132
133 /**
134 * Similar to TLM's debug transport
135 */
136 void
137 SCSlavePort::recvFunctional(PacketPtr packet)
138 {
139 /* Prepare the transaction */
140 tlm::tlm_generic_payload * trans = mm.allocate();
141 trans->acquire();
142 packet2payload(packet, *trans);
143
144 /* Attach the packet pointer to the TLM transaction to keep track */
145 Gem5Extension* extension = new Gem5Extension(packet);
146 trans->set_auto_extension(extension);
147
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");
152 }
153
154 trans->release();
155 }
156
157 bool
158 SCSlavePort::recvTimingSnoopResp(PacketPtr packet)
159 {
160 /* Snooping should be implemented with tlm_dbg_transport */
161 SC_REPORT_FATAL("SCSlavePort","unimplemented func.: recvTimingSnoopResp");
162 return false;
163 }
164
165 void
166 SCSlavePort::recvFunctionalSnoop(PacketPtr packet)
167 {
168 /* Snooping should be implemented with tlm_dbg_transport */
169 SC_REPORT_FATAL("SCSlavePort","unimplemented func.: recvFunctionalSnoop");
170 }
171
172 /**
173 * Similar to TLM's non-blocking transport (AT)
174 */
175 bool
176 SCSlavePort::recvTimingReq(PacketPtr packet)
177 {
178 CAUGHT_UP;
179
180 panic_if(packet->cacheResponding(), "Should not see packets where cache "
181 "is responding");
182
183 panic_if(!(packet->isRead() || packet->isWrite()),
184 "Should only see read and writes at TLM memory\n");
185
186
187 /* We should never get a second request after noting that a retry is
188 * required */
189 sc_assert(!needToSendRequestRetry);
190
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;
195 return false;
196 }
197
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
200 * in the protocol:
201 * if (requestInProgress)
202 * {
203 * wait(endRequestEvent);
204 * }
205 * requestInProgress = trans;
206 */
207
208 /* Prepare the transaction */
209 tlm::tlm_generic_payload * trans = mm.allocate();
210 trans->acquire();
211 packet2payload(packet, *trans);
212
213 /* Attach the packet pointer to the TLM transaction to keep track */
214 Gem5Extension* extension = new Gem5Extension(packet);
215 trans->set_auto_extension(extension);
216
217 /*
218 * Pay for annotated transport delays.
219 *
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.
223 *
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).
233 *
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.
237 */
238 auto delay = sc_core::sc_time::from_value(packet->payloadDelay);
239 // reset the delays
240 packet->payloadDelay = 0;
241 packet->headerDelay = 0;
242
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);
256
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);
264 trans->release();
265 }
266
267 return true;
268 }
269
270 void
271 SCSlavePort::pec(
272 PayloadEvent<SCSlavePort> * pe,
273 tlm::tlm_generic_payload& trans,
274 const tlm::tlm_phase& phase)
275 {
276 sc_time delay;
277
278 if (phase == tlm::END_REQ ||
279 &trans == blockingRequest && phase == tlm::BEGIN_RESP) {
280 sc_assert(&trans == blockingRequest);
281 blockingRequest = NULL;
282
283 /* Did another request arrive while blocked, schedule a retry */
284 if (needToSendRequestRetry) {
285 needToSendRequestRetry = false;
286 sendRetryReq();
287 }
288 }
289 if (phase == tlm::BEGIN_RESP)
290 {
291 CAUGHT_UP;
292
293 auto& extension = Gem5Extension::getExtension(trans);
294 auto packet = extension.getPacket();
295
296 sc_assert(!blockingResponse);
297
298 bool need_retry = false;
299
300 /*
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
304 * packet.
305 */
306 if (extension.isPipeThrough()) {
307 if (packet->isResponse()) {
308 need_retry = !sendTimingResp(packet);
309 }
310 } else if (packet->needsResponse()) {
311 packet->makeResponse();
312 need_retry = !sendTimingResp(packet);
313 }
314
315 if (need_retry) {
316 blockingResponse = &trans;
317 } else {
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 */
324 trans.release();
325 }
326 }
327 }
328 delete pe;
329 }
330
331 void
332 SCSlavePort::recvRespRetry()
333 {
334 CAUGHT_UP;
335
336 /* Retry a response */
337 sc_assert(blockingResponse);
338
339 tlm::tlm_generic_payload *trans = blockingResponse;
340 blockingResponse = NULL;
341 PacketPtr packet = Gem5Extension::getExtension(trans).getPacket();
342
343 bool need_retry = !sendTimingResp(packet);
344
345 sc_assert(!need_retry);
346
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
351 trans->release();
352 }
353
354 tlm::tlm_sync_enum
355 SCSlavePort::nb_transport_bw(tlm::tlm_generic_payload& trans,
356 tlm::tlm_phase& phase,
357 sc_core::sc_time& delay)
358 {
359 PayloadEvent<SCSlavePort> * pe;
360 pe = new PayloadEvent<SCSlavePort>(*this, &SCSlavePort::pec, "PE");
361 pe->notify(trans, phase, delay);
362 return tlm::TLM_ACCEPTED;
363 }
364
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),
372 transactor(nullptr)
373 {
374 }
375
376 void
377 SCSlavePort::bindToTransactor(Gem5SlaveTransactor* transactor)
378 {
379 sc_assert(this->transactor == nullptr);
380
381 this->transactor = transactor;
382
383 transactor->socket.register_nb_transport_bw(this,
384 &SCSlavePort::nb_transport_bw);
385 }
386
387 ExternalSlave::Port*
388 SCSlavePortHandler::getExternalPort(const std::string &name,
389 ExternalSlave &owner,
390 const std::string &port_data)
391 {
392 // Create and register a new SystemC slave port
393 auto* port = new SCSlavePort(name, port_data, owner);
394
395 control.registerSlavePort(port_data, port);
396
397 return port;
398 }
399
400 }