RubyPort.cc (10115:0e0a0dd558db) RubyPort.cc (10117:37e333de580f)
1/*
2 * Copyright (c) 2012-2013 ARM Limited
3 * All rights reserved.
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder. You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Copyright (c) 2009 Advanced Micro Devices, Inc.
15 * Copyright (c) 2011 Mark D. Hill and David A. Wood
16 * All rights reserved.
17 *
18 * Redistribution and use in source and binary forms, with or without
19 * modification, are permitted provided that the following conditions are
20 * met: redistributions of source code must retain the above copyright
21 * notice, this list of conditions and the following disclaimer;
22 * redistributions in binary form must reproduce the above copyright
23 * notice, this list of conditions and the following disclaimer in the
24 * documentation and/or other materials provided with the distribution;
25 * neither the name of the copyright holders nor the names of its
26 * contributors may be used to endorse or promote products derived from
27 * this software without specific prior written permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40 */
41
42#include "cpu/testers/rubytest/RubyTester.hh"
43#include "debug/Config.hh"
44#include "debug/Drain.hh"
45#include "debug/Ruby.hh"
46#include "mem/protocol/AccessPermission.hh"
47#include "mem/ruby/slicc_interface/AbstractController.hh"
48#include "mem/ruby/system/RubyPort.hh"
1/*
2 * Copyright (c) 2012-2013 ARM Limited
3 * All rights reserved.
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder. You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Copyright (c) 2009 Advanced Micro Devices, Inc.
15 * Copyright (c) 2011 Mark D. Hill and David A. Wood
16 * All rights reserved.
17 *
18 * Redistribution and use in source and binary forms, with or without
19 * modification, are permitted provided that the following conditions are
20 * met: redistributions of source code must retain the above copyright
21 * notice, this list of conditions and the following disclaimer;
22 * redistributions in binary form must reproduce the above copyright
23 * notice, this list of conditions and the following disclaimer in the
24 * documentation and/or other materials provided with the distribution;
25 * neither the name of the copyright holders nor the names of its
26 * contributors may be used to endorse or promote products derived from
27 * this software without specific prior written permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40 */
41
42#include "cpu/testers/rubytest/RubyTester.hh"
43#include "debug/Config.hh"
44#include "debug/Drain.hh"
45#include "debug/Ruby.hh"
46#include "mem/protocol/AccessPermission.hh"
47#include "mem/ruby/slicc_interface/AbstractController.hh"
48#include "mem/ruby/system/RubyPort.hh"
49#include "sim/full_system.hh"
49#include "sim/system.hh"
50
51RubyPort::RubyPort(const Params *p)
52 : MemObject(p), m_version(p->version), m_controller(NULL),
53 m_mandatory_q_ptr(NULL), m_usingRubyTester(p->using_ruby_tester),
54 pioMasterPort(csprintf("%s.pio-master-port", name()), this),
55 pioSlavePort(csprintf("%s.pio-slave-port", name()), this),
56 memMasterPort(csprintf("%s.mem-master-port", name()), this),
57 memSlavePort(csprintf("%s-mem-slave-port", name()), this,
58 p->ruby_system, p->access_phys_mem, -1),
59 gotAddrRanges(p->port_master_connection_count), drainManager(NULL),
60 system(p->system), access_phys_mem(p->access_phys_mem)
61{
62 assert(m_version != -1);
63
64 // create the slave ports based on the number of connected ports
65 for (size_t i = 0; i < p->port_slave_connection_count; ++i) {
66 slave_ports.push_back(new MemSlavePort(csprintf("%s.slave%d", name(),
67 i), this, p->ruby_system, access_phys_mem, i));
68 }
69
70 // create the master ports based on the number of connected ports
71 for (size_t i = 0; i < p->port_master_connection_count; ++i) {
72 master_ports.push_back(new PioMasterPort(csprintf("%s.master%d",
73 name(), i), this));
74 }
75}
76
77void
78RubyPort::init()
79{
80 assert(m_controller != NULL);
81 m_mandatory_q_ptr = m_controller->getMandatoryQueue();
82 m_mandatory_q_ptr->setSender(this);
83}
84
85BaseMasterPort &
86RubyPort::getMasterPort(const std::string &if_name, PortID idx)
87{
88 if (if_name == "mem_master_port") {
89 return memMasterPort;
90 }
91
92 if (if_name == "pio_master_port") {
93 return pioMasterPort;
94 }
95
96 // used by the x86 CPUs to connect the interrupt PIO and interrupt slave
97 // port
98 if (if_name != "master") {
99 // pass it along to our super class
100 return MemObject::getMasterPort(if_name, idx);
101 } else {
102 if (idx >= static_cast<PortID>(master_ports.size())) {
103 panic("RubyPort::getMasterPort: unknown index %d\n", idx);
104 }
105
106 return *master_ports[idx];
107 }
108}
109
110BaseSlavePort &
111RubyPort::getSlavePort(const std::string &if_name, PortID idx)
112{
113 if (if_name == "mem_slave_port") {
114 return memSlavePort;
115 }
116
117 if (if_name == "pio_slave_port")
118 return pioSlavePort;
119
120 // used by the CPUs to connect the caches to the interconnect, and
121 // for the x86 case also the interrupt master
122 if (if_name != "slave") {
123 // pass it along to our super class
124 return MemObject::getSlavePort(if_name, idx);
125 } else {
126 if (idx >= static_cast<PortID>(slave_ports.size())) {
127 panic("RubyPort::getSlavePort: unknown index %d\n", idx);
128 }
129
130 return *slave_ports[idx];
131 }
132}
133
134RubyPort::PioMasterPort::PioMasterPort(const std::string &_name,
135 RubyPort *_port)
136 : QueuedMasterPort(_name, _port, queue), queue(*_port, *this)
137{
138 DPRINTF(RubyPort, "Created master pioport on sequencer %s\n", _name);
139}
140
141RubyPort::PioSlavePort::PioSlavePort(const std::string &_name,
142 RubyPort *_port)
143 : QueuedSlavePort(_name, _port, queue), queue(*_port, *this)
144{
145 DPRINTF(RubyPort, "Created slave pioport on sequencer %s\n", _name);
146}
147
148RubyPort::MemMasterPort::MemMasterPort(const std::string &_name,
149 RubyPort *_port)
150 : QueuedMasterPort(_name, _port, queue), queue(*_port, *this)
151{
152 DPRINTF(RubyPort, "Created master memport on ruby sequencer %s\n", _name);
153}
154
155RubyPort::MemSlavePort::MemSlavePort(const std::string &_name, RubyPort *_port,
156 RubySystem *_system, bool _access_phys_mem, PortID id)
157 : QueuedSlavePort(_name, _port, queue, id), queue(*_port, *this),
158 ruby_system(_system), access_phys_mem(_access_phys_mem)
159{
160 DPRINTF(RubyPort, "Created slave memport on ruby sequencer %s\n", _name);
161}
162
163bool
164RubyPort::PioMasterPort::recvTimingResp(PacketPtr pkt)
165{
166 RubyPort *ruby_port = static_cast<RubyPort *>(&owner);
167 DPRINTF(RubyPort, "Response for address: 0x%#x\n", pkt->getAddr());
168
169 // send next cycle
170 ruby_port->pioSlavePort.schedTimingResp(
171 pkt, curTick() + g_system_ptr->clockPeriod());
172 return true;
173}
174
175bool RubyPort::MemMasterPort::recvTimingResp(PacketPtr pkt)
176{
177 // got a response from a device
178 assert(pkt->isResponse());
179
180 // In FS mode, ruby memory will receive pio responses from devices
181 // and it must forward these responses back to the particular CPU.
182 DPRINTF(RubyPort, "Pio response for address %#x, going to %d\n",
183 pkt->getAddr(), pkt->getDest());
184
185 // First we must retrieve the request port from the sender State
186 RubyPort::SenderState *senderState =
187 safe_cast<RubyPort::SenderState *>(pkt->popSenderState());
188 MemSlavePort *port = senderState->port;
189 assert(port != NULL);
190 delete senderState;
191
192 // attempt to send the response in the next cycle
193 port->schedTimingResp(pkt, curTick() + g_system_ptr->clockPeriod());
194
195 return true;
196}
197
198bool
199RubyPort::PioSlavePort::recvTimingReq(PacketPtr pkt)
200{
201 RubyPort *ruby_port = static_cast<RubyPort *>(&owner);
202
203 for (size_t i = 0; i < ruby_port->master_ports.size(); ++i) {
204 AddrRangeList l = ruby_port->master_ports[i]->getAddrRanges();
205 for (auto it = l.begin(); it != l.end(); ++it) {
206 if (it->contains(pkt->getAddr())) {
207 ruby_port->master_ports[i]->sendTimingReq(pkt);
208 return true;
209 }
210 }
211 }
212 panic("Should never reach here!\n");
213}
214
215bool
216RubyPort::MemSlavePort::recvTimingReq(PacketPtr pkt)
217{
218 DPRINTF(RubyPort, "Timing request for address %#x on port %d\n",
219 pkt->getAddr(), id);
220 RubyPort *ruby_port = static_cast<RubyPort *>(&owner);
221
222 if (pkt->memInhibitAsserted())
223 panic("RubyPort should never see an inhibited request\n");
224
225 // Check for pio requests and directly send them to the dedicated
226 // pio port.
227 if (!isPhysMemAddress(pkt->getAddr())) {
228 assert(ruby_port->memMasterPort.isConnected());
229 DPRINTF(RubyPort, "Request address %#x assumed to be a pio address\n",
230 pkt->getAddr());
231
232 // Save the port in the sender state object to be used later to
233 // route the response
234 pkt->pushSenderState(new SenderState(this));
235
236 // send next cycle
237 ruby_port->memMasterPort.schedTimingReq(pkt,
238 curTick() + g_system_ptr->clockPeriod());
239 return true;
240 }
241
242 // Save the port id to be used later to route the response
243 pkt->setSrc(id);
244
245 assert(Address(pkt->getAddr()).getOffset() + pkt->getSize() <=
246 RubySystem::getBlockSizeBytes());
247
248 // Submit the ruby request
249 RequestStatus requestStatus = ruby_port->makeRequest(pkt);
250
251 // If the request successfully issued then we should return true.
252 // Otherwise, we need to tell the port to retry at a later point
253 // and return false.
254 if (requestStatus == RequestStatus_Issued) {
255 DPRINTF(RubyPort, "Request %s 0x%x issued\n", pkt->cmdString(),
256 pkt->getAddr());
257 return true;
258 }
259
260 //
261 // Unless one is using the ruby tester, record the stalled M5 port for
262 // later retry when the sequencer becomes free.
263 //
264 if (!ruby_port->m_usingRubyTester) {
265 ruby_port->addToRetryList(this);
266 }
267
268 DPRINTF(RubyPort, "Request for address %#x did not issued because %s\n",
269 pkt->getAddr(), RequestStatus_to_string(requestStatus));
270
271 return false;
272}
273
274void
275RubyPort::MemSlavePort::recvFunctional(PacketPtr pkt)
276{
277 DPRINTF(RubyPort, "Functional access for address: %#x\n", pkt->getAddr());
278 RubyPort *ruby_port = static_cast<RubyPort *>(&owner);
279
280 // Check for pio requests and directly send them to the dedicated
281 // pio port.
282 if (!isPhysMemAddress(pkt->getAddr())) {
283 assert(ruby_port->memMasterPort.isConnected());
284 DPRINTF(RubyPort, "Pio Request for address: 0x%#x\n", pkt->getAddr());
285 panic("RubyPort::PioMasterPort::recvFunctional() not implemented!\n");
286 }
287
288 assert(pkt->getAddr() + pkt->getSize() <=
289 line_address(Address(pkt->getAddr())).getAddress() +
290 RubySystem::getBlockSizeBytes());
291
292 bool accessSucceeded = false;
293 bool needsResponse = pkt->needsResponse();
294
295 // Do the functional access on ruby memory
296 if (pkt->isRead()) {
297 accessSucceeded = ruby_system->functionalRead(pkt);
298 } else if (pkt->isWrite()) {
299 accessSucceeded = ruby_system->functionalWrite(pkt);
300 } else {
301 panic("Unsupported functional command %s\n", pkt->cmdString());
302 }
303
304 // Unless the requester explicitly said otherwise, generate an error if
305 // the functional request failed
306 if (!accessSucceeded && !pkt->suppressFuncError()) {
307 fatal("Ruby functional %s failed for address %#x\n",
308 pkt->isWrite() ? "write" : "read", pkt->getAddr());
309 }
310
311 if (access_phys_mem) {
312 // The attached physmem contains the official version of data.
313 // The following command performs the real functional access.
314 // This line should be removed once Ruby supplies the official version
315 // of data.
316 ruby_port->system->getPhysMem().functionalAccess(pkt);
317 }
318
319 // turn packet around to go back to requester if response expected
320 if (needsResponse) {
321 pkt->setFunctionalResponseStatus(accessSucceeded);
322
323 // @todo There should not be a reverse call since the response is
324 // communicated through the packet pointer
325 // DPRINTF(RubyPort, "Sending packet back over port\n");
326 // sendFunctional(pkt);
327 }
328 DPRINTF(RubyPort, "Functional access %s!\n",
329 accessSucceeded ? "successful":"failed");
330}
331
332void
333RubyPort::ruby_hit_callback(PacketPtr pkt)
334{
335 DPRINTF(RubyPort, "Hit callback for %s 0x%x\n", pkt->cmdString(),
336 pkt->getAddr());
337
338 // The packet was destined for memory and has not yet been turned
339 // into a response
340 assert(system->isMemAddr(pkt->getAddr()));
341 assert(pkt->isRequest());
342
343 // As it has not yet been turned around, the source field tells us
344 // which port it came from.
345 assert(pkt->getSrc() < slave_ports.size());
346
347 slave_ports[pkt->getSrc()]->hitCallback(pkt);
348
349 //
350 // If we had to stall the MemSlavePorts, wake them up because the sequencer
351 // likely has free resources now.
352 //
353 if (!retryList.empty()) {
354 //
355 // Record the current list of ports to retry on a temporary list before
356 // calling sendRetry on those ports. sendRetry will cause an
357 // immediate retry, which may result in the ports being put back on the
358 // list. Therefore we want to clear the retryList before calling
359 // sendRetry.
360 //
361 std::vector<MemSlavePort *> curRetryList(retryList);
362
363 retryList.clear();
364
365 for (auto i = curRetryList.begin(); i != curRetryList.end(); ++i) {
366 DPRINTF(RubyPort,
367 "Sequencer may now be free. SendRetry to port %s\n",
368 (*i)->name());
369 (*i)->sendRetry();
370 }
371 }
372
373 testDrainComplete();
374}
375
376void
377RubyPort::testDrainComplete()
378{
379 //If we weren't able to drain before, we might be able to now.
380 if (drainManager != NULL) {
381 unsigned int drainCount = outstandingCount();
382 DPRINTF(Drain, "Drain count: %u\n", drainCount);
383 if (drainCount == 0) {
384 DPRINTF(Drain, "RubyPort done draining, signaling drain done\n");
385 drainManager->signalDrainDone();
386 // Clear the drain manager once we're done with it.
387 drainManager = NULL;
388 }
389 }
390}
391
392unsigned int
393RubyPort::getChildDrainCount(DrainManager *dm)
394{
395 int count = 0;
396
397 if (memMasterPort.isConnected()) {
398 count += memMasterPort.drain(dm);
399 DPRINTF(Config, "count after pio check %d\n", count);
400 }
401
402 for (CpuPortIter p = slave_ports.begin(); p != slave_ports.end(); ++p) {
403 count += (*p)->drain(dm);
404 DPRINTF(Config, "count after slave port check %d\n", count);
405 }
406
407 for (std::vector<PioMasterPort *>::iterator p = master_ports.begin();
408 p != master_ports.end(); ++p) {
409 count += (*p)->drain(dm);
410 DPRINTF(Config, "count after master port check %d\n", count);
411 }
412
413 DPRINTF(Config, "final count %d\n", count);
414 return count;
415}
416
417unsigned int
418RubyPort::drain(DrainManager *dm)
419{
420 if (isDeadlockEventScheduled()) {
421 descheduleDeadlockEvent();
422 }
423
424 //
425 // If the RubyPort is not empty, then it needs to clear all outstanding
426 // requests before it should call drainManager->signalDrainDone()
427 //
428 DPRINTF(Config, "outstanding count %d\n", outstandingCount());
429 bool need_drain = outstandingCount() > 0;
430
431 //
432 // Also, get the number of child ports that will also need to clear
433 // their buffered requests before they call drainManager->signalDrainDone()
434 //
435 unsigned int child_drain_count = getChildDrainCount(dm);
436
437 // Set status
438 if (need_drain) {
439 drainManager = dm;
440
441 DPRINTF(Drain, "RubyPort not drained\n");
442 setDrainState(Drainable::Draining);
443 return child_drain_count + 1;
444 }
445
446 drainManager = NULL;
447 setDrainState(Drainable::Drained);
448 return child_drain_count;
449}
450
451void
452RubyPort::MemSlavePort::hitCallback(PacketPtr pkt)
453{
454 bool needsResponse = pkt->needsResponse();
455
456 //
457 // Unless specified at configuraiton, all responses except failed SC
458 // and Flush operations access M5 physical memory.
459 //
460 bool accessPhysMem = access_phys_mem;
461
462 if (pkt->isLLSC()) {
463 if (pkt->isWrite()) {
464 if (pkt->req->getExtraData() != 0) {
465 //
466 // Successful SC packets convert to normal writes
467 //
468 pkt->convertScToWrite();
469 } else {
470 //
471 // Failed SC packets don't access physical memory and thus
472 // the RubyPort itself must convert it to a response.
473 //
474 accessPhysMem = false;
475 }
476 } else {
477 //
478 // All LL packets convert to normal loads so that M5 PhysMem does
479 // not lock the blocks.
480 //
481 pkt->convertLlToRead();
482 }
483 }
484
485 //
486 // Flush requests don't access physical memory
487 //
488 if (pkt->isFlush()) {
489 accessPhysMem = false;
490 }
491
492 DPRINTF(RubyPort, "Hit callback needs response %d\n", needsResponse);
493
494 if (accessPhysMem) {
495 RubyPort *ruby_port = static_cast<RubyPort *>(&owner);
496 ruby_port->system->getPhysMem().access(pkt);
497 } else if (needsResponse) {
498 pkt->makeResponse();
499 }
500
501 // turn packet around to go back to requester if response expected
502 if (needsResponse) {
503 DPRINTF(RubyPort, "Sending packet back over port\n");
504 // send next cycle
505 schedTimingResp(pkt, curTick() + g_system_ptr->clockPeriod());
506 } else {
507 delete pkt;
508 }
509 DPRINTF(RubyPort, "Hit callback done!\n");
510}
511
512AddrRangeList
513RubyPort::PioSlavePort::getAddrRanges() const
514{
515 // at the moment the assumption is that the master does not care
516 AddrRangeList ranges;
517 RubyPort *ruby_port = static_cast<RubyPort *>(&owner);
518
519 for (size_t i = 0; i < ruby_port->master_ports.size(); ++i) {
520 ranges.splice(ranges.begin(),
521 ruby_port->master_ports[i]->getAddrRanges());
522 }
523 for (AddrRangeConstIter r = ranges.begin(); r != ranges.end(); ++r)
524 DPRINTF(RubyPort, "%s\n", r->to_string());
525 return ranges;
526}
527
528bool
529RubyPort::MemSlavePort::isPhysMemAddress(Addr addr) const
530{
531 RubyPort *ruby_port = static_cast<RubyPort *>(&owner);
532 return ruby_port->system->isMemAddr(addr);
533}
534
535void
536RubyPort::ruby_eviction_callback(const Address& address)
537{
538 DPRINTF(RubyPort, "Sending invalidations.\n");
539 // This request is deleted in the stack-allocated packet destructor
540 // when this function exits
541 // TODO: should this really be using funcMasterId?
542 RequestPtr req =
543 new Request(address.getAddress(), 0, 0, Request::funcMasterId);
544 // Use a single packet to signal all snooping ports of the invalidation.
545 // This assumes that snooping ports do NOT modify the packet/request
546 Packet pkt(req, MemCmd::InvalidationReq);
547 for (CpuPortIter p = slave_ports.begin(); p != slave_ports.end(); ++p) {
548 // check if the connected master port is snooping
549 if ((*p)->isSnooping()) {
550 // send as a snoop request
551 (*p)->sendTimingSnoopReq(&pkt);
552 }
553 }
554}
555
556void
557RubyPort::PioMasterPort::recvRangeChange()
558{
559 RubyPort &r = static_cast<RubyPort &>(owner);
560 r.gotAddrRanges--;
50#include "sim/system.hh"
51
52RubyPort::RubyPort(const Params *p)
53 : MemObject(p), m_version(p->version), m_controller(NULL),
54 m_mandatory_q_ptr(NULL), m_usingRubyTester(p->using_ruby_tester),
55 pioMasterPort(csprintf("%s.pio-master-port", name()), this),
56 pioSlavePort(csprintf("%s.pio-slave-port", name()), this),
57 memMasterPort(csprintf("%s.mem-master-port", name()), this),
58 memSlavePort(csprintf("%s-mem-slave-port", name()), this,
59 p->ruby_system, p->access_phys_mem, -1),
60 gotAddrRanges(p->port_master_connection_count), drainManager(NULL),
61 system(p->system), access_phys_mem(p->access_phys_mem)
62{
63 assert(m_version != -1);
64
65 // create the slave ports based on the number of connected ports
66 for (size_t i = 0; i < p->port_slave_connection_count; ++i) {
67 slave_ports.push_back(new MemSlavePort(csprintf("%s.slave%d", name(),
68 i), this, p->ruby_system, access_phys_mem, i));
69 }
70
71 // create the master ports based on the number of connected ports
72 for (size_t i = 0; i < p->port_master_connection_count; ++i) {
73 master_ports.push_back(new PioMasterPort(csprintf("%s.master%d",
74 name(), i), this));
75 }
76}
77
78void
79RubyPort::init()
80{
81 assert(m_controller != NULL);
82 m_mandatory_q_ptr = m_controller->getMandatoryQueue();
83 m_mandatory_q_ptr->setSender(this);
84}
85
86BaseMasterPort &
87RubyPort::getMasterPort(const std::string &if_name, PortID idx)
88{
89 if (if_name == "mem_master_port") {
90 return memMasterPort;
91 }
92
93 if (if_name == "pio_master_port") {
94 return pioMasterPort;
95 }
96
97 // used by the x86 CPUs to connect the interrupt PIO and interrupt slave
98 // port
99 if (if_name != "master") {
100 // pass it along to our super class
101 return MemObject::getMasterPort(if_name, idx);
102 } else {
103 if (idx >= static_cast<PortID>(master_ports.size())) {
104 panic("RubyPort::getMasterPort: unknown index %d\n", idx);
105 }
106
107 return *master_ports[idx];
108 }
109}
110
111BaseSlavePort &
112RubyPort::getSlavePort(const std::string &if_name, PortID idx)
113{
114 if (if_name == "mem_slave_port") {
115 return memSlavePort;
116 }
117
118 if (if_name == "pio_slave_port")
119 return pioSlavePort;
120
121 // used by the CPUs to connect the caches to the interconnect, and
122 // for the x86 case also the interrupt master
123 if (if_name != "slave") {
124 // pass it along to our super class
125 return MemObject::getSlavePort(if_name, idx);
126 } else {
127 if (idx >= static_cast<PortID>(slave_ports.size())) {
128 panic("RubyPort::getSlavePort: unknown index %d\n", idx);
129 }
130
131 return *slave_ports[idx];
132 }
133}
134
135RubyPort::PioMasterPort::PioMasterPort(const std::string &_name,
136 RubyPort *_port)
137 : QueuedMasterPort(_name, _port, queue), queue(*_port, *this)
138{
139 DPRINTF(RubyPort, "Created master pioport on sequencer %s\n", _name);
140}
141
142RubyPort::PioSlavePort::PioSlavePort(const std::string &_name,
143 RubyPort *_port)
144 : QueuedSlavePort(_name, _port, queue), queue(*_port, *this)
145{
146 DPRINTF(RubyPort, "Created slave pioport on sequencer %s\n", _name);
147}
148
149RubyPort::MemMasterPort::MemMasterPort(const std::string &_name,
150 RubyPort *_port)
151 : QueuedMasterPort(_name, _port, queue), queue(*_port, *this)
152{
153 DPRINTF(RubyPort, "Created master memport on ruby sequencer %s\n", _name);
154}
155
156RubyPort::MemSlavePort::MemSlavePort(const std::string &_name, RubyPort *_port,
157 RubySystem *_system, bool _access_phys_mem, PortID id)
158 : QueuedSlavePort(_name, _port, queue, id), queue(*_port, *this),
159 ruby_system(_system), access_phys_mem(_access_phys_mem)
160{
161 DPRINTF(RubyPort, "Created slave memport on ruby sequencer %s\n", _name);
162}
163
164bool
165RubyPort::PioMasterPort::recvTimingResp(PacketPtr pkt)
166{
167 RubyPort *ruby_port = static_cast<RubyPort *>(&owner);
168 DPRINTF(RubyPort, "Response for address: 0x%#x\n", pkt->getAddr());
169
170 // send next cycle
171 ruby_port->pioSlavePort.schedTimingResp(
172 pkt, curTick() + g_system_ptr->clockPeriod());
173 return true;
174}
175
176bool RubyPort::MemMasterPort::recvTimingResp(PacketPtr pkt)
177{
178 // got a response from a device
179 assert(pkt->isResponse());
180
181 // In FS mode, ruby memory will receive pio responses from devices
182 // and it must forward these responses back to the particular CPU.
183 DPRINTF(RubyPort, "Pio response for address %#x, going to %d\n",
184 pkt->getAddr(), pkt->getDest());
185
186 // First we must retrieve the request port from the sender State
187 RubyPort::SenderState *senderState =
188 safe_cast<RubyPort::SenderState *>(pkt->popSenderState());
189 MemSlavePort *port = senderState->port;
190 assert(port != NULL);
191 delete senderState;
192
193 // attempt to send the response in the next cycle
194 port->schedTimingResp(pkt, curTick() + g_system_ptr->clockPeriod());
195
196 return true;
197}
198
199bool
200RubyPort::PioSlavePort::recvTimingReq(PacketPtr pkt)
201{
202 RubyPort *ruby_port = static_cast<RubyPort *>(&owner);
203
204 for (size_t i = 0; i < ruby_port->master_ports.size(); ++i) {
205 AddrRangeList l = ruby_port->master_ports[i]->getAddrRanges();
206 for (auto it = l.begin(); it != l.end(); ++it) {
207 if (it->contains(pkt->getAddr())) {
208 ruby_port->master_ports[i]->sendTimingReq(pkt);
209 return true;
210 }
211 }
212 }
213 panic("Should never reach here!\n");
214}
215
216bool
217RubyPort::MemSlavePort::recvTimingReq(PacketPtr pkt)
218{
219 DPRINTF(RubyPort, "Timing request for address %#x on port %d\n",
220 pkt->getAddr(), id);
221 RubyPort *ruby_port = static_cast<RubyPort *>(&owner);
222
223 if (pkt->memInhibitAsserted())
224 panic("RubyPort should never see an inhibited request\n");
225
226 // Check for pio requests and directly send them to the dedicated
227 // pio port.
228 if (!isPhysMemAddress(pkt->getAddr())) {
229 assert(ruby_port->memMasterPort.isConnected());
230 DPRINTF(RubyPort, "Request address %#x assumed to be a pio address\n",
231 pkt->getAddr());
232
233 // Save the port in the sender state object to be used later to
234 // route the response
235 pkt->pushSenderState(new SenderState(this));
236
237 // send next cycle
238 ruby_port->memMasterPort.schedTimingReq(pkt,
239 curTick() + g_system_ptr->clockPeriod());
240 return true;
241 }
242
243 // Save the port id to be used later to route the response
244 pkt->setSrc(id);
245
246 assert(Address(pkt->getAddr()).getOffset() + pkt->getSize() <=
247 RubySystem::getBlockSizeBytes());
248
249 // Submit the ruby request
250 RequestStatus requestStatus = ruby_port->makeRequest(pkt);
251
252 // If the request successfully issued then we should return true.
253 // Otherwise, we need to tell the port to retry at a later point
254 // and return false.
255 if (requestStatus == RequestStatus_Issued) {
256 DPRINTF(RubyPort, "Request %s 0x%x issued\n", pkt->cmdString(),
257 pkt->getAddr());
258 return true;
259 }
260
261 //
262 // Unless one is using the ruby tester, record the stalled M5 port for
263 // later retry when the sequencer becomes free.
264 //
265 if (!ruby_port->m_usingRubyTester) {
266 ruby_port->addToRetryList(this);
267 }
268
269 DPRINTF(RubyPort, "Request for address %#x did not issued because %s\n",
270 pkt->getAddr(), RequestStatus_to_string(requestStatus));
271
272 return false;
273}
274
275void
276RubyPort::MemSlavePort::recvFunctional(PacketPtr pkt)
277{
278 DPRINTF(RubyPort, "Functional access for address: %#x\n", pkt->getAddr());
279 RubyPort *ruby_port = static_cast<RubyPort *>(&owner);
280
281 // Check for pio requests and directly send them to the dedicated
282 // pio port.
283 if (!isPhysMemAddress(pkt->getAddr())) {
284 assert(ruby_port->memMasterPort.isConnected());
285 DPRINTF(RubyPort, "Pio Request for address: 0x%#x\n", pkt->getAddr());
286 panic("RubyPort::PioMasterPort::recvFunctional() not implemented!\n");
287 }
288
289 assert(pkt->getAddr() + pkt->getSize() <=
290 line_address(Address(pkt->getAddr())).getAddress() +
291 RubySystem::getBlockSizeBytes());
292
293 bool accessSucceeded = false;
294 bool needsResponse = pkt->needsResponse();
295
296 // Do the functional access on ruby memory
297 if (pkt->isRead()) {
298 accessSucceeded = ruby_system->functionalRead(pkt);
299 } else if (pkt->isWrite()) {
300 accessSucceeded = ruby_system->functionalWrite(pkt);
301 } else {
302 panic("Unsupported functional command %s\n", pkt->cmdString());
303 }
304
305 // Unless the requester explicitly said otherwise, generate an error if
306 // the functional request failed
307 if (!accessSucceeded && !pkt->suppressFuncError()) {
308 fatal("Ruby functional %s failed for address %#x\n",
309 pkt->isWrite() ? "write" : "read", pkt->getAddr());
310 }
311
312 if (access_phys_mem) {
313 // The attached physmem contains the official version of data.
314 // The following command performs the real functional access.
315 // This line should be removed once Ruby supplies the official version
316 // of data.
317 ruby_port->system->getPhysMem().functionalAccess(pkt);
318 }
319
320 // turn packet around to go back to requester if response expected
321 if (needsResponse) {
322 pkt->setFunctionalResponseStatus(accessSucceeded);
323
324 // @todo There should not be a reverse call since the response is
325 // communicated through the packet pointer
326 // DPRINTF(RubyPort, "Sending packet back over port\n");
327 // sendFunctional(pkt);
328 }
329 DPRINTF(RubyPort, "Functional access %s!\n",
330 accessSucceeded ? "successful":"failed");
331}
332
333void
334RubyPort::ruby_hit_callback(PacketPtr pkt)
335{
336 DPRINTF(RubyPort, "Hit callback for %s 0x%x\n", pkt->cmdString(),
337 pkt->getAddr());
338
339 // The packet was destined for memory and has not yet been turned
340 // into a response
341 assert(system->isMemAddr(pkt->getAddr()));
342 assert(pkt->isRequest());
343
344 // As it has not yet been turned around, the source field tells us
345 // which port it came from.
346 assert(pkt->getSrc() < slave_ports.size());
347
348 slave_ports[pkt->getSrc()]->hitCallback(pkt);
349
350 //
351 // If we had to stall the MemSlavePorts, wake them up because the sequencer
352 // likely has free resources now.
353 //
354 if (!retryList.empty()) {
355 //
356 // Record the current list of ports to retry on a temporary list before
357 // calling sendRetry on those ports. sendRetry will cause an
358 // immediate retry, which may result in the ports being put back on the
359 // list. Therefore we want to clear the retryList before calling
360 // sendRetry.
361 //
362 std::vector<MemSlavePort *> curRetryList(retryList);
363
364 retryList.clear();
365
366 for (auto i = curRetryList.begin(); i != curRetryList.end(); ++i) {
367 DPRINTF(RubyPort,
368 "Sequencer may now be free. SendRetry to port %s\n",
369 (*i)->name());
370 (*i)->sendRetry();
371 }
372 }
373
374 testDrainComplete();
375}
376
377void
378RubyPort::testDrainComplete()
379{
380 //If we weren't able to drain before, we might be able to now.
381 if (drainManager != NULL) {
382 unsigned int drainCount = outstandingCount();
383 DPRINTF(Drain, "Drain count: %u\n", drainCount);
384 if (drainCount == 0) {
385 DPRINTF(Drain, "RubyPort done draining, signaling drain done\n");
386 drainManager->signalDrainDone();
387 // Clear the drain manager once we're done with it.
388 drainManager = NULL;
389 }
390 }
391}
392
393unsigned int
394RubyPort::getChildDrainCount(DrainManager *dm)
395{
396 int count = 0;
397
398 if (memMasterPort.isConnected()) {
399 count += memMasterPort.drain(dm);
400 DPRINTF(Config, "count after pio check %d\n", count);
401 }
402
403 for (CpuPortIter p = slave_ports.begin(); p != slave_ports.end(); ++p) {
404 count += (*p)->drain(dm);
405 DPRINTF(Config, "count after slave port check %d\n", count);
406 }
407
408 for (std::vector<PioMasterPort *>::iterator p = master_ports.begin();
409 p != master_ports.end(); ++p) {
410 count += (*p)->drain(dm);
411 DPRINTF(Config, "count after master port check %d\n", count);
412 }
413
414 DPRINTF(Config, "final count %d\n", count);
415 return count;
416}
417
418unsigned int
419RubyPort::drain(DrainManager *dm)
420{
421 if (isDeadlockEventScheduled()) {
422 descheduleDeadlockEvent();
423 }
424
425 //
426 // If the RubyPort is not empty, then it needs to clear all outstanding
427 // requests before it should call drainManager->signalDrainDone()
428 //
429 DPRINTF(Config, "outstanding count %d\n", outstandingCount());
430 bool need_drain = outstandingCount() > 0;
431
432 //
433 // Also, get the number of child ports that will also need to clear
434 // their buffered requests before they call drainManager->signalDrainDone()
435 //
436 unsigned int child_drain_count = getChildDrainCount(dm);
437
438 // Set status
439 if (need_drain) {
440 drainManager = dm;
441
442 DPRINTF(Drain, "RubyPort not drained\n");
443 setDrainState(Drainable::Draining);
444 return child_drain_count + 1;
445 }
446
447 drainManager = NULL;
448 setDrainState(Drainable::Drained);
449 return child_drain_count;
450}
451
452void
453RubyPort::MemSlavePort::hitCallback(PacketPtr pkt)
454{
455 bool needsResponse = pkt->needsResponse();
456
457 //
458 // Unless specified at configuraiton, all responses except failed SC
459 // and Flush operations access M5 physical memory.
460 //
461 bool accessPhysMem = access_phys_mem;
462
463 if (pkt->isLLSC()) {
464 if (pkt->isWrite()) {
465 if (pkt->req->getExtraData() != 0) {
466 //
467 // Successful SC packets convert to normal writes
468 //
469 pkt->convertScToWrite();
470 } else {
471 //
472 // Failed SC packets don't access physical memory and thus
473 // the RubyPort itself must convert it to a response.
474 //
475 accessPhysMem = false;
476 }
477 } else {
478 //
479 // All LL packets convert to normal loads so that M5 PhysMem does
480 // not lock the blocks.
481 //
482 pkt->convertLlToRead();
483 }
484 }
485
486 //
487 // Flush requests don't access physical memory
488 //
489 if (pkt->isFlush()) {
490 accessPhysMem = false;
491 }
492
493 DPRINTF(RubyPort, "Hit callback needs response %d\n", needsResponse);
494
495 if (accessPhysMem) {
496 RubyPort *ruby_port = static_cast<RubyPort *>(&owner);
497 ruby_port->system->getPhysMem().access(pkt);
498 } else if (needsResponse) {
499 pkt->makeResponse();
500 }
501
502 // turn packet around to go back to requester if response expected
503 if (needsResponse) {
504 DPRINTF(RubyPort, "Sending packet back over port\n");
505 // send next cycle
506 schedTimingResp(pkt, curTick() + g_system_ptr->clockPeriod());
507 } else {
508 delete pkt;
509 }
510 DPRINTF(RubyPort, "Hit callback done!\n");
511}
512
513AddrRangeList
514RubyPort::PioSlavePort::getAddrRanges() const
515{
516 // at the moment the assumption is that the master does not care
517 AddrRangeList ranges;
518 RubyPort *ruby_port = static_cast<RubyPort *>(&owner);
519
520 for (size_t i = 0; i < ruby_port->master_ports.size(); ++i) {
521 ranges.splice(ranges.begin(),
522 ruby_port->master_ports[i]->getAddrRanges());
523 }
524 for (AddrRangeConstIter r = ranges.begin(); r != ranges.end(); ++r)
525 DPRINTF(RubyPort, "%s\n", r->to_string());
526 return ranges;
527}
528
529bool
530RubyPort::MemSlavePort::isPhysMemAddress(Addr addr) const
531{
532 RubyPort *ruby_port = static_cast<RubyPort *>(&owner);
533 return ruby_port->system->isMemAddr(addr);
534}
535
536void
537RubyPort::ruby_eviction_callback(const Address& address)
538{
539 DPRINTF(RubyPort, "Sending invalidations.\n");
540 // This request is deleted in the stack-allocated packet destructor
541 // when this function exits
542 // TODO: should this really be using funcMasterId?
543 RequestPtr req =
544 new Request(address.getAddress(), 0, 0, Request::funcMasterId);
545 // Use a single packet to signal all snooping ports of the invalidation.
546 // This assumes that snooping ports do NOT modify the packet/request
547 Packet pkt(req, MemCmd::InvalidationReq);
548 for (CpuPortIter p = slave_ports.begin(); p != slave_ports.end(); ++p) {
549 // check if the connected master port is snooping
550 if ((*p)->isSnooping()) {
551 // send as a snoop request
552 (*p)->sendTimingSnoopReq(&pkt);
553 }
554 }
555}
556
557void
558RubyPort::PioMasterPort::recvRangeChange()
559{
560 RubyPort &r = static_cast<RubyPort &>(owner);
561 r.gotAddrRanges--;
561 if (r.gotAddrRanges == 0) {
562 if (r.gotAddrRanges == 0 && FullSystem) {
562 r.pioSlavePort.sendRangeChange();
563 }
564}
563 r.pioSlavePort.sendRangeChange();
564 }
565}