04efce87b7d18995a1a1942541282d16aa07ca9a
[gem5.git] / util / tlm / sc_port.cc
1 /*
2 * Copyright (c) 2015, University of Kaiserslautern
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * 1. Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * 3. Neither the name of the copyright holder nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
24 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 * Authors: Matthias Jung
33 * Abdul Mutaal Ahmad
34 */
35
36 #include <cctype>
37 #include <iomanip>
38 #include <sstream>
39
40 #include "debug/ExternalPort.hh"
41 #include "sc_ext.hh"
42 #include "sc_mm.hh"
43 #include "sc_port.hh"
44
45 namespace Gem5SystemC
46 {
47
48 /**
49 * Instantiate a tlm memory manager that takes care about all the
50 * tlm transactions in the system
51 */
52 MemoryManager mm;
53
54 /**
55 * Convert a gem5 packet to a TLM payload by copying all the relevant
56 * information to a previously allocated tlm payload
57 */
58 void
59 packet2payload(PacketPtr packet, tlm::tlm_generic_payload &trans)
60 {
61 trans.set_address(packet->getAddr());
62
63 /* Check if this transaction was allocated by mm */
64 sc_assert(trans.has_mm());
65
66 unsigned int size = packet->getSize();
67 unsigned char *data = packet->getPtr<unsigned char>();
68
69 trans.set_data_length(size);
70 trans.set_streaming_width(size);
71 trans.set_data_ptr(data);
72
73 if (packet->isRead()) {
74 trans.set_command(tlm::TLM_READ_COMMAND);
75 }
76 else if (packet->isInvalidate()) {
77 /* Do nothing */
78 } else if (packet->isWrite()) {
79 trans.set_command(tlm::TLM_WRITE_COMMAND);
80 } else {
81 SC_REPORT_FATAL("transactor", "No R/W packet");
82 }
83 }
84
85 /**
86 * Similar to TLM's blocking transport (LT)
87 */
88 Tick
89 sc_transactor::recvAtomic(PacketPtr packet)
90 {
91 CAUGHT_UP;
92 SC_REPORT_INFO("transactor", "recvAtomic hasn't been tested much");
93
94 sc_core::sc_time delay = sc_core::SC_ZERO_TIME;
95
96
97 /* Prepare the transaction */
98 tlm::tlm_generic_payload * trans = mm.allocate();
99 trans->acquire();
100 packet2payload(packet, *trans);
101
102 /* Attach the packet pointer to the TLM transaction to keep track */
103 gem5Extension* extension = new gem5Extension(packet);
104 trans->set_auto_extension(extension);
105
106 /* Execute b_transport: */
107 if (packet->cmd == MemCmd::SwapReq) {
108 SC_REPORT_FATAL("transactor", "SwapReq not supported");
109 } else if (packet->isRead()) {
110 iSocket->b_transport(*trans, delay);
111 } else if (packet->isInvalidate()) {
112 // do nothing
113 } else if (packet->isWrite()) {
114 iSocket->b_transport(*trans, delay);
115 } else {
116 SC_REPORT_FATAL("transactor", "Typo of request not supported");
117 }
118
119 if (packet->needsResponse()) {
120 packet->makeResponse();
121 }
122
123 trans->release();
124
125 return delay.value();
126 }
127
128 /**
129 * Similar to TLM's debug transport
130 */
131 void
132 sc_transactor::recvFunctional(PacketPtr packet)
133 {
134 /* Prepare the transaction */
135 tlm::tlm_generic_payload * trans = mm.allocate();
136 trans->acquire();
137 packet2payload(packet, *trans);
138
139 /* Attach the packet pointer to the TLM transaction to keep track */
140 gem5Extension* extension = new gem5Extension(packet);
141 trans->set_auto_extension(extension);
142
143 /* Execute Debug Transport: */
144 unsigned int bytes = iSocket->transport_dbg(*trans);
145 if (bytes != trans->get_data_length()) {
146 SC_REPORT_FATAL("transactor","debug transport was not completed");
147 }
148
149 trans->release();
150 }
151
152 bool
153 sc_transactor::recvTimingSnoopResp(PacketPtr packet)
154 {
155 /* Snooping should be implemented with tlm_dbg_transport */
156 SC_REPORT_FATAL("transactor","unimplemented func.: recvTimingSnoopResp");
157 return false;
158 }
159
160 void
161 sc_transactor::recvFunctionalSnoop(PacketPtr packet)
162 {
163 /* Snooping should be implemented with tlm_dbg_transport */
164 SC_REPORT_FATAL("transactor","unimplemented func.: recvFunctionalSnoop");
165 }
166
167 /**
168 * Similar to TLM's non-blocking transport (AT)
169 */
170 bool
171 sc_transactor::recvTimingReq(PacketPtr packet)
172 {
173 CAUGHT_UP;
174
175 /* We should never get a second request after noting that a retry is
176 * required */
177 sc_assert(!needToSendRequestRetry);
178
179 // simply drop inhibited packets and clean evictions
180 if (packet->memInhibitAsserted() ||
181 packet->cmd == MemCmd::CleanEvict)
182 return true;
183
184 /* Remember if a request comes in while we're blocked so that a retry
185 * can be sent to gem5 */
186 if (blockingRequest) {
187 needToSendRequestRetry = true;
188 return false;
189 }
190
191 /* NOTE: normal tlm is blocking here. But in our case we return false
192 * and tell gem5 when a retry can be done. This is the main difference
193 * in the protocol:
194 * if (requestInProgress)
195 * {
196 * wait(endRequestEvent);
197 * }
198 * requestInProgress = trans;
199 */
200
201 /* Prepare the transaction */
202 tlm::tlm_generic_payload * trans = mm.allocate();
203 trans->acquire();
204 packet2payload(packet, *trans);
205
206 /* Attach the packet pointer to the TLM transaction to keep track */
207 gem5Extension* extension = new gem5Extension(packet);
208 trans->set_auto_extension(extension);
209
210 /* Starting TLM non-blocking sequence (AT) Refer to IEEE1666-2011 SystemC
211 * Standard Page 507 for a visualisation of the procedure */
212 sc_core::sc_time delay = sc_core::SC_ZERO_TIME;
213 tlm::tlm_phase phase = tlm::BEGIN_REQ;
214 tlm::tlm_sync_enum status;
215 status = iSocket->nb_transport_fw(*trans, phase, delay);
216 /* Check returned value: */
217 if (status == tlm::TLM_ACCEPTED) {
218 sc_assert(phase == tlm::BEGIN_REQ);
219 /* Accepted but is now blocking until END_REQ (exclusion rule)*/
220 blockingRequest = trans;
221 } else if (status == tlm::TLM_UPDATED) {
222 /* The Timing annotation must be honored: */
223 sc_assert(phase == tlm::END_REQ || phase == tlm::BEGIN_RESP);
224
225 payloadEvent<sc_transactor> * pe;
226 pe = new payloadEvent<sc_transactor>(*this,
227 &sc_transactor::pec, "PEQ");
228 pe->notify(*trans, phase, delay);
229 } else if (status == tlm::TLM_COMPLETED) {
230 /* Transaction is over nothing has do be done. */
231 sc_assert(phase == tlm::END_RESP);
232 trans->release();
233 }
234
235 return true;
236 }
237
238 void
239 sc_transactor::pec(
240 sc_transactor::payloadEvent<sc_transactor> * pe,
241 tlm::tlm_generic_payload& trans,
242 const tlm::tlm_phase& phase)
243 {
244 sc_time delay;
245
246 if (phase == tlm::END_REQ ||
247 &trans == blockingRequest && phase == tlm::BEGIN_RESP) {
248 sc_assert(&trans == blockingRequest);
249 blockingRequest = NULL;
250
251 /* Did another request arrive while blocked, schedule a retry */
252 if (needToSendRequestRetry) {
253 needToSendRequestRetry = false;
254 iSocket.sendRetryReq();
255 }
256 }
257 else if (phase == tlm::BEGIN_RESP)
258 {
259 CAUGHT_UP;
260
261 PacketPtr packet = gem5Extension::getExtension(trans).getPacket();
262
263 sc_assert(!blockingResponse);
264
265 bool need_retry;
266 if (packet->needsResponse()) {
267 packet->makeResponse();
268 need_retry = !iSocket.sendTimingResp(packet);
269 } else {
270 need_retry = false;
271 }
272
273 if (need_retry) {
274 blockingResponse = &trans;
275 } else {
276 if (phase == tlm::BEGIN_RESP) {
277 /* Send END_RESP and we're finished: */
278 tlm::tlm_phase fw_phase = tlm::END_RESP;
279 sc_time delay = SC_ZERO_TIME;
280 iSocket->nb_transport_fw(trans, fw_phase, delay);
281 /* Release the transaction with all the extensions */
282 trans.release();
283 }
284 }
285 } else {
286 SC_REPORT_FATAL("transactor", "Invalid protocol phase in pec");
287 }
288 delete pe;
289 }
290
291 void
292 sc_transactor::recvRespRetry()
293 {
294 CAUGHT_UP;
295
296 /* Retry a response */
297 sc_assert(blockingResponse);
298
299 tlm::tlm_generic_payload *trans = blockingResponse;
300 blockingResponse = NULL;
301 PacketPtr packet = gem5Extension::getExtension(trans).getPacket();
302
303 bool need_retry = !iSocket.sendTimingResp(packet);
304
305 sc_assert(!need_retry);
306
307 sc_core::sc_time delay = sc_core::SC_ZERO_TIME;
308 tlm::tlm_phase phase = tlm::END_RESP;
309 iSocket->nb_transport_fw(*trans, phase, delay);
310 // Release transaction with all the extensions
311 trans->release();
312 }
313
314 tlm::tlm_sync_enum
315 sc_transactor::nb_transport_bw(tlm::tlm_generic_payload& trans,
316 tlm::tlm_phase& phase,
317 sc_core::sc_time& delay)
318 {
319 payloadEvent<sc_transactor> * pe;
320 pe = new payloadEvent<sc_transactor>(*this, &sc_transactor::pec, "PE");
321 pe->notify(trans, phase, delay);
322 return tlm::TLM_ACCEPTED;
323 }
324
325 void
326 sc_transactor::invalidate_direct_mem_ptr(sc_dt::uint64 start_range,
327 sc_dt::uint64 end_range)
328 {
329 SC_REPORT_FATAL("transactor", "unimpl. func: invalidate_direct_mem_ptr");
330 }
331
332 sc_transactor::sc_transactor(const std::string &name_,
333 const std::string &systemc_name,
334 ExternalSlave &owner_) :
335 tlm::tlm_initiator_socket<>(systemc_name.c_str()),
336 ExternalSlave::Port(name_, owner_),
337 iSocket(*this),
338 blockingRequest(NULL),
339 needToSendRequestRetry(false),
340 blockingResponse(NULL)
341 {
342 m_export.bind(*this);
343 }
344
345 class sc_transactorHandler : public ExternalSlave::Handler
346 {
347 public:
348 ExternalSlave::Port *getExternalPort(const std::string &name,
349 ExternalSlave &owner,
350 const std::string &port_data)
351 {
352 // This will make a new initiatiator port
353 return new sc_transactor(name, port_data, owner);
354 }
355 };
356
357 void
358 registerSCPorts()
359 {
360 ExternalSlave::registerHandler("tlm", new sc_transactorHandler);
361 }
362
363 }