MEM: Add the communication monitor
[gem5.git] / src / mem / comm_monitor.cc
1 /*
2 * Copyright (c) 2012 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 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions are
16 * met: redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer;
18 * redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution;
21 * neither the name of the copyright holders nor the names of its
22 * contributors may be used to endorse or promote products derived from
23 * this software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 *
37 * Authors: Thomas Grass
38 * Andreas Hansson
39 */
40
41 #include "debug/CommMonitor.hh"
42 #include "mem/comm_monitor.hh"
43 #include "sim/stats.hh"
44
45 CommMonitor::CommMonitor(Params* params)
46 : MemObject(params),
47 masterPort(name() + "-master", *this),
48 slavePort(name() + "-slave", *this),
49 samplePeriodicEvent(this),
50 samplePeriodTicks(params->sample_period),
51 readAddrMask(params->read_addr_mask),
52 writeAddrMask(params->write_addr_mask),
53 stats(params)
54 {
55 // keep track of the sample period both in ticks and absolute time
56 samplePeriod.setTick(params->sample_period);
57
58 DPRINTF(CommMonitor,
59 "Created monitor %s with sample period %d ticks (%f s)\n",
60 name(), samplePeriodTicks, samplePeriod);
61 }
62
63 CommMonitor*
64 CommMonitorParams::create()
65 {
66 return new CommMonitor(this);
67 }
68
69 void
70 CommMonitor::init()
71 {
72 // make sure both sides of the monitor are connected
73 if (!slavePort.isConnected() || !masterPort.isConnected())
74 fatal("Communication monitor is not connected on both sides.\n");
75 }
76
77 MasterPort&
78 CommMonitor::getMasterPort(const std::string& if_name, int idx)
79 {
80 if (if_name == "master") {
81 return masterPort;
82 } else {
83 return MemObject::getMasterPort(if_name, idx);
84 }
85 }
86
87 SlavePort&
88 CommMonitor::getSlavePort(const std::string& if_name, int idx)
89 {
90 if (if_name == "slave") {
91 return slavePort;
92 } else {
93 return MemObject::getSlavePort(if_name, idx);
94 }
95 }
96
97 void
98 CommMonitor::recvFunctional(PacketPtr pkt)
99 {
100 masterPort.sendFunctional(pkt);
101 }
102
103 void
104 CommMonitor::recvFunctionalSnoop(PacketPtr pkt)
105 {
106 slavePort.sendFunctionalSnoop(pkt);
107 }
108
109 Tick
110 CommMonitor::recvAtomic(PacketPtr pkt)
111 {
112 return masterPort.sendAtomic(pkt);
113 }
114
115 Tick
116 CommMonitor::recvAtomicSnoop(PacketPtr pkt)
117 {
118 return slavePort.sendAtomicSnoop(pkt);
119 }
120
121 bool
122 CommMonitor::recvTimingReq(PacketPtr pkt)
123 {
124 // should always see a request
125 assert(pkt->isRequest());
126
127 // Store relevant fields of packet, because packet may be modified
128 // or even deleted when sendTiming() is called.
129 bool isRead = pkt->isRead();
130 bool isWrite = pkt->isWrite();
131 unsigned size = pkt->getSize();
132 Addr addr = pkt->getAddr();
133 bool needsResponse = pkt->needsResponse();
134 bool memInhibitAsserted = pkt->memInhibitAsserted();
135 Packet::SenderState* senderState = pkt->senderState;
136
137 // If a cache miss is served by a cache, a monitor near the memory
138 // would see a request which needs a response, but this response
139 // would be inhibited and not come back from the memory. Therefore
140 // we additionally have to check the inhibit flag.
141 if (needsResponse && !memInhibitAsserted && !stats.disableLatencyHists) {
142 pkt->senderState = new CommMonitorSenderState(senderState,
143 curTick());
144 }
145
146 // Attempt to send the packet (always succeeds for inhibited
147 // packets)
148 bool successful = masterPort.sendTimingReq(pkt);
149
150 // If not successful, restore the sender state
151 if (!successful && needsResponse && !stats.disableLatencyHists) {
152 delete pkt->senderState;
153 pkt->senderState = senderState;
154 }
155
156 if (successful && isRead) {
157 DPRINTF(CommMonitor, "Forwarded read request\n");
158
159 // Increment number of observed read transactions
160 if (!stats.disableTransactionHists) {
161 ++stats.readTrans;
162 }
163
164 // Get sample of burst length
165 if (!stats.disableBurstLengthHists) {
166 stats.readBurstLengthHist.sample(size);
167 }
168
169 // Sample the masked address
170 if (!stats.disableAddrDists) {
171 stats.readAddrDist.sample(addr & readAddrMask);
172 }
173
174 // If it needs a response increment number of outstanding read
175 // requests
176 if (!stats.disableOutstandingHists && needsResponse) {
177 ++stats.outstandingReadReqs;
178 }
179
180 if (!stats.disableITTDists) {
181 // Sample value of read-read inter transaction time
182 if (stats.timeOfLastRead != 0) {
183 stats.ittReadRead.sample(curTick() - stats.timeOfLastRead);
184 }
185 stats.timeOfLastRead = curTick();
186
187 // Sample value of req-req inter transaction time
188 if (stats.timeOfLastReq != 0) {
189 stats.ittReqReq.sample(curTick() - stats.timeOfLastReq);
190 }
191 stats.timeOfLastReq = curTick();
192 }
193 } else if (successful && isWrite) {
194 DPRINTF(CommMonitor, "Forwarded write request\n");
195
196 // Same as for reads
197 if (!stats.disableTransactionHists) {
198 ++stats.writeTrans;
199 }
200
201 if (!stats.disableBurstLengthHists) {
202 stats.writeBurstLengthHist.sample(size);
203 }
204
205 // Update the bandwidth stats on the request
206 if (!stats.disableBandwidthHists) {
207 stats.writtenBytes += size;
208 stats.totalWrittenBytes += size;
209 }
210
211 // Sample the masked write address
212 if (!stats.disableAddrDists) {
213 stats.writeAddrDist.sample(addr & writeAddrMask);
214 }
215
216 if (!stats.disableOutstandingHists && needsResponse) {
217 ++stats.outstandingWriteReqs;
218 }
219
220 if (!stats.disableITTDists) {
221 // Sample value of write-to-write inter transaction time
222 if (stats.timeOfLastWrite != 0) {
223 stats.ittWriteWrite.sample(curTick() - stats.timeOfLastWrite);
224 }
225 stats.timeOfLastWrite = curTick();
226
227 // Sample value of req-to-req inter transaction time
228 if (stats.timeOfLastReq != 0) {
229 stats.ittReqReq.sample(curTick() - stats.timeOfLastReq);
230 }
231 stats.timeOfLastReq = curTick();
232 }
233 } else if (successful) {
234 DPRINTF(CommMonitor, "Forwarded non read/write request\n");
235 }
236
237 return successful;
238 }
239
240 bool
241 CommMonitor::recvTimingResp(PacketPtr pkt)
242 {
243 // should always see responses
244 assert(pkt->isResponse());
245
246 // Store relevant fields of packet, because packet may be modified
247 // or even deleted when sendTiming() is called.
248 bool isRead = pkt->isRead();
249 bool isWrite = pkt->isWrite();
250 unsigned size = pkt->getSize();
251 Tick latency = 0;
252 CommMonitorSenderState* commReceivedState =
253 dynamic_cast<CommMonitorSenderState*>(pkt->senderState);
254
255 if (!stats.disableLatencyHists) {
256 // Restore initial sender state
257 if (commReceivedState == NULL)
258 panic("Monitor got a response without monitor sender state\n");
259
260 // Restore the sate
261 pkt->senderState = commReceivedState->origSenderState;
262 }
263
264 // Attempt to send the packet
265 bool successful = slavePort.sendTimingResp(pkt);
266
267 if (!stats.disableLatencyHists) {
268 // If packet successfully send, sample value of latency,
269 // afterwards delete sender state, otherwise restore state
270 if (successful) {
271 latency = curTick() - commReceivedState->transmitTime;
272 DPRINTF(CommMonitor, "Latency: %d\n", latency);
273 delete commReceivedState;
274 } else {
275 // Don't delete anything and let the packet look like we
276 // did not touch it
277 pkt->senderState = commReceivedState;
278 }
279 }
280
281 if (successful && isRead) {
282 // Decrement number of outstanding read requests
283 DPRINTF(CommMonitor, "Received read response\n");
284 if (!stats.disableOutstandingHists) {
285 assert(stats.outstandingReadReqs != 0);
286 --stats.outstandingReadReqs;
287 }
288
289 if (!stats.disableLatencyHists) {
290 stats.readLatencyHist.sample(latency);
291 }
292
293 // Update the bandwidth stats based on responses for reads
294 if (!stats.disableBandwidthHists) {
295 stats.readBytes += size;
296 stats.totalReadBytes += size;
297 }
298
299 } else if (successful && isWrite) {
300 // Decrement number of outstanding write requests
301 DPRINTF(CommMonitor, "Received write response\n");
302 if (!stats.disableOutstandingHists) {
303 assert(stats.outstandingWriteReqs != 0);
304 --stats.outstandingWriteReqs;
305 }
306
307 if (!stats.disableLatencyHists) {
308 stats.writeLatencyHist.sample(latency);
309 }
310 } else if (successful) {
311 DPRINTF(CommMonitor, "Received non read/write response\n");
312 }
313 return successful;
314 }
315
316 void
317 CommMonitor::recvTimingSnoopReq(PacketPtr pkt)
318 {
319 slavePort.sendTimingSnoopReq(pkt);
320 }
321
322 bool
323 CommMonitor::recvTimingSnoopResp(PacketPtr pkt)
324 {
325 return masterPort.sendTimingSnoopResp(pkt);
326 }
327
328 bool
329 CommMonitor::isSnooping() const
330 {
331 return slavePort.getMasterPort().isSnooping();
332 }
333
334 unsigned
335 CommMonitor::deviceBlockSizeMaster()
336 {
337 return slavePort.peerBlockSize();
338 }
339
340 unsigned
341 CommMonitor::deviceBlockSizeSlave()
342 {
343 return masterPort.peerBlockSize();
344 }
345
346 AddrRangeList
347 CommMonitor::getAddrRanges()
348 {
349 return masterPort.getSlavePort().getAddrRanges();
350 }
351
352 void
353 CommMonitor::recvRetryMaster()
354 {
355 slavePort.sendRetry();
356 }
357
358 void
359 CommMonitor::recvRetrySlave()
360 {
361 masterPort.sendRetry();
362 }
363
364 void
365 CommMonitor::recvRangeChange()
366 {
367 slavePort.sendRangeChange();
368 }
369
370 void
371 CommMonitor::regStats()
372 {
373 // Initialise all the monitor stats
374 using namespace Stats;
375
376 stats.readBurstLengthHist
377 .init(params()->burst_length_bins)
378 .name(name() + ".readBurstLengthHist")
379 .desc("Histogram of burst lengths of transmitted packets")
380 .flags(stats.disableBurstLengthHists ? nozero : pdf);
381
382 stats.writeBurstLengthHist
383 .init(params()->burst_length_bins)
384 .name(name() + ".writeBurstLengthHist")
385 .desc("Histogram of burst lengths of transmitted packets")
386 .flags(stats.disableBurstLengthHists ? nozero : pdf);
387
388 // Stats based on received responses
389 stats.readBandwidthHist
390 .init(params()->bandwidth_bins)
391 .name(name() + ".readBandwidthHist")
392 .desc("Histogram of read bandwidth per sample period (bytes/s)")
393 .flags(stats.disableBandwidthHists ? nozero : pdf);
394
395 stats.averageReadBW
396 .name(name() + ".averageReadBandwidth")
397 .desc("Average read bandwidth (bytes/s)")
398 .flags(stats.disableBandwidthHists ? nozero : pdf);
399
400 stats.totalReadBytes
401 .name(name() + ".totalReadBytes")
402 .desc("Number of bytes read")
403 .flags(stats.disableBandwidthHists ? nozero : pdf);
404
405 stats.averageReadBW = stats.totalReadBytes / simSeconds;
406
407 // Stats based on successfully sent requests
408 stats.writeBandwidthHist
409 .init(params()->bandwidth_bins)
410 .name(name() + ".writeBandwidthHist")
411 .desc("Histogram of write bandwidth (bytes/s)")
412 .flags(stats.disableBandwidthHists ? (pdf | nozero) : pdf);
413
414 stats.averageWriteBW
415 .name(name() + ".averageWriteBandwidth")
416 .desc("Average write bandwidth (bytes/s)")
417 .flags(stats.disableBandwidthHists ? nozero : pdf);
418
419 stats.totalWrittenBytes
420 .name(name() + ".totalWrittenBytes")
421 .desc("Number of bytes written")
422 .flags(stats.disableBandwidthHists ? nozero : pdf);
423
424 stats.averageWriteBW = stats.totalWrittenBytes / simSeconds;
425
426 stats.readLatencyHist
427 .init(params()->latency_bins)
428 .name(name() + ".readLatencyHist")
429 .desc("Read request-response latency")
430 .flags(stats.disableLatencyHists ? nozero : pdf);
431
432 stats.writeLatencyHist
433 .init(params()->latency_bins)
434 .name(name() + ".writeLatencyHist")
435 .desc("Write request-response latency")
436 .flags(stats.disableLatencyHists ? nozero : pdf);
437
438 stats.ittReadRead
439 .init(1, params()->itt_max_bin, params()->itt_max_bin /
440 params()->itt_bins)
441 .name(name() + ".ittReadRead")
442 .desc("Read-to-read inter transaction time")
443 .flags(stats.disableITTDists ? nozero : pdf);
444
445 stats.ittWriteWrite
446 .init(1, params()->itt_max_bin, params()->itt_max_bin /
447 params()->itt_bins)
448 .name(name() + ".ittWriteWrite")
449 .desc("Write-to-write inter transaction time")
450 .flags(stats.disableITTDists ? nozero : pdf);
451
452 stats.ittReqReq
453 .init(1, params()->itt_max_bin, params()->itt_max_bin /
454 params()->itt_bins)
455 .name(name() + ".ittReqReq")
456 .desc("Request-to-request inter transaction time")
457 .flags(stats.disableITTDists ? nozero : pdf);
458
459 stats.outstandingReadsHist
460 .init(params()->outstanding_bins)
461 .name(name() + ".outstandingReadsHist")
462 .desc("Outstanding read transactions")
463 .flags(stats.disableOutstandingHists ? nozero : pdf);
464
465 stats.outstandingWritesHist
466 .init(params()->outstanding_bins)
467 .name(name() + ".outstandingWritesHist")
468 .desc("Outstanding write transactions")
469 .flags(stats.disableOutstandingHists ? nozero : pdf);
470
471 stats.readTransHist
472 .init(params()->transaction_bins)
473 .name(name() + ".readTransHist")
474 .desc("Histogram of read transactions per sample period")
475 .flags(stats.disableTransactionHists ? nozero : pdf);
476
477 stats.writeTransHist
478 .init(params()->transaction_bins)
479 .name(name() + ".writeTransHist")
480 .desc("Histogram of read transactions per sample period")
481 .flags(stats.disableTransactionHists ? nozero : pdf);
482
483 stats.readAddrDist
484 .init(0)
485 .name(name() + ".readAddrDist")
486 .desc("Read address distribution")
487 .flags(stats.disableAddrDists ? nozero : pdf);
488
489 stats.writeAddrDist
490 .init(0)
491 .name(name() + ".writeAddrDist")
492 .desc("Write address distribution")
493 .flags(stats.disableAddrDists ? nozero : pdf);
494 }
495
496 void
497 CommMonitor::samplePeriodic()
498 {
499 // the periodic stats update runs on the granularity of sample
500 // periods, but in combination with this there may also be a
501 // external resets and dumps of the stats (through schedStatEvent)
502 // causing the stats themselves to capture less than a sample
503 // period
504
505 // only capture if we have not reset the stats during the last
506 // sample period
507 if (simTicks.value() >= samplePeriodTicks) {
508 if (!stats.disableTransactionHists) {
509 stats.readTransHist.sample(stats.readTrans);
510 stats.writeTransHist.sample(stats.writeTrans);
511 }
512
513 if (!stats.disableBandwidthHists) {
514 stats.readBandwidthHist.sample(stats.readBytes / samplePeriod);
515 stats.writeBandwidthHist.sample(stats.writtenBytes / samplePeriod);
516 }
517
518 if (!stats.disableOutstandingHists) {
519 stats.outstandingReadsHist.sample(stats.outstandingReadReqs);
520 stats.outstandingWritesHist.sample(stats.outstandingWriteReqs);
521 }
522 }
523
524 // reset the sampled values
525 stats.readTrans = 0;
526 stats.writeTrans = 0;
527
528 stats.readBytes = 0;
529 stats.writtenBytes = 0;
530
531 schedule(samplePeriodicEvent, curTick() + samplePeriodTicks);
532 }
533
534 void
535 CommMonitor::startup()
536 {
537 schedule(samplePeriodicEvent, curTick() + samplePeriodTicks);
538 }