SST 12.1.0
Structural Simulation Toolkit
stdMem.h
1// -*- mode: c++ -*-
2// Copyright 2009-2022 NTESS. Under the terms
3// of Contract DE-NA0003525 with NTESS, the U.S.
4// Government retains certain rights in this software.
5//
6// Copyright (c) 2009-2022, NTESS
7// All rights reserved.
8//
9// This file is part of the SST software package. For license
10// information, see the LICENSE file in the top level directory of the
11// distribution.
12//
13
14#ifndef SST_CORE_INTERFACES_STANDARDMEM_H
15#define SST_CORE_INTERFACES_STANDARDMEM_H
16
17#include "sst/core/link.h"
18#include "sst/core/params.h"
19#include "sst/core/sst_types.h"
20#include "sst/core/ssthandler.h"
21#include "sst/core/subcomponent.h"
22#include "sst/core/warnmacros.h"
23
24#include <atomic>
25#include <map>
26#include <string>
27#include <utility>
28
29namespace SST {
30
31class Component;
32class Event;
33
34namespace Interfaces {
35/**
36 * Generic interface to Memory models
37 *
38 * Implementation notes
39 * Instructions can be sent into a memory system using derivatives of Request().
40 * This interface can be used by both compute hosts (e.g., CPUs) and MMIO devices (e.g., accelerator).
41 * Not all interfaces/memory systems support all Request types. The interface should return
42 * an error if it encounters an unhandled type.
43 *
44 * Request class:
45 * - Uses a separate class for each request type
46 * - Additional classes can be defined outside sst-core to add custom types
47 * - Requests and responses share the same ID
48 * - req.makeResponse() should be used to generate a correctly populated response event.
49 * - Any additional fields that need to be set manually are noted in function comments
50 * - req.needsResponse() should be used to determine whether a response should be sent.
51 *
52 * Built-in commands
53 * Basic:
54 * - Reads, writes
55 * - Noncacheable reads, writes
56 * Flushes:
57 * - By address: flush and flush-invalidate
58 * Atomic updates:
59 * - Read-lock, Write-unlock
60 * - Load-link, Store-conditional
61 * Data movement:
62 * - Data move (copy data from one memory location to another, e.g., for scratchpad)
63 * Notifications:
64 * - Cache invalidation
65 * Custom:
66 * - CustomRequest, this is intended to be extended by users
67 * - CustomResponse, this is intended to be extended by users
68 */
70{
71public:
72 class Request; // Needed for HandlerBase definition
73 /**
74 Base handler for request handling.
75 */
77
78 /**
79 Used to create handlers for request handling. The callback
80 function is expected to be in the form of:
81
82 void func(Request* event)
83
84 In which case, the class is created with:
85
86 new StdMem::Handler<classname>(this, &classname::function_name)
87
88 Or, to add static data, the callback function is:
89
90 void func(Request* event, dataT data)
91
92 and the class is created with:
93
94 new stdMem::Handler<classname, dataT>(this, &classname::function_name, data)
95 */
96 template <typename classT, typename dataT = void>
98
99 class RequestConverter; // Convert request to SST::Event* according to type
100 class RequestHandler; // Handle a request according to type
101
102 SST_ELI_REGISTER_SUBCOMPONENT_API(SST::Interfaces::StandardMem,TimeConverter*,HandlerBase*)
103
104 /** All Addresses can be 64-bit */
105 typedef uint64_t Addr;
106
107 /**
108 * Base class for StandardMem commands
109 */
111 {
112 public:
113 typedef uint64_t id_t;
114 typedef uint32_t flags_t;
115
116 /** Flags that modify requests.
117 * Each bit in a 32-bit field (flags_t) defines a seperate flag.
118 * Values less than F_RESERVED are reserved for futgure expansion.
119 * Users may define custom flags above F_RESERVED
120 */
121 enum class Flag {
122 F_NONCACHEABLE = 1 << 1, /* Bypass caches for this event */
123 F_FAIL = 1 << 2, /* For events that can fail, this indicates failure. */
124 F_TRACE = 1 << 3, /* This flag requests that debug/trace output be generated for this event if possible. */
125 F_RESERVED = 1 << 16, /* Flags <= F_RESERVED are reserved for future expansion */
126 };
127
128 Request(flags_t fl = 0)
129 {
130 id = main_id++;
131 flags = fl;
132 } /* Requests get a new ID */
133 Request(id_t rid, flags_t flags = 0) :
134 id(rid),
135 flags(flags) {} /* Responses share an ID with the matching request */
136
137 virtual ~Request() {}
138
139 id_t getID() { return id; }
140
141 virtual Request*
142 makeResponse() = 0; // Helper for properly formatting responses; returns nullptr if no response type exists
143 virtual bool needsResponse() = 0; // Indicates whether event requires a response
144
145 /* Convert Request to an Event type - intended to be called by standardInterface */
146 virtual SST::Event* convert(RequestConverter* converter) = 0;
147
148 virtual void handle(RequestHandler* handler) = 0;
149
150 /* Return string representation of event for debug/output/etc. */
151 virtual std::string getString() = 0;
152
153 /* Flag handling */
154 void setNoncacheable() { flags |= static_cast<int>(Flag::F_NONCACHEABLE); }
155 void unsetNoncacheable() { flags &= ~(static_cast<int>(Flag::F_NONCACHEABLE)); }
156 bool getNoncacheable() { return flags & static_cast<int>(Flag::F_NONCACHEABLE); }
157
158 void setSuccess() { unsetFail(); }
159 void unsetSuccess() { setFail(); }
160 bool getSuccess() { return ~(flags & static_cast<int>(Flag::F_FAIL)); }
161 bool getFail() { return flags & static_cast<int>(Flag::F_FAIL); }
162 void setFail() { flags |= static_cast<int>(Flag::F_FAIL); }
163 void unsetFail() { flags &= ~(static_cast<int>(Flag::F_FAIL)); }
164
165 void setTrace() { flags |= static_cast<int>(Flag::F_TRACE); }
166 void unsetTrace() { flags &= (~static_cast<int>(Flag::F_TRACE)); }
167 bool getTrace() { return flags & static_cast<int>(Flag::F_TRACE); }
168
169 void setFlag(flags_t flag) { flags |= flag; }
170 void setFlag(Flag flag) { flags |= static_cast<flags_t>(flag); }
171 void unsetFlag(flags_t flag) { flags &= (~flag); }
172 void unsetFlag(Flag flag) { flags &= ~(static_cast<flags_t>(flag)); }
173 bool getFlag(flags_t flag) { return flags & flag; }
174 bool getFlag(Flag flag) { return flags & static_cast<flags_t>(flag); }
175
176 void clearAllFlags() { flags = 0; }
177 flags_t getAllFlags() { return flags; }
178
179 std::string getFlagString()
180 {
181 /* Print flag name for known ones and F_XX where XX is the bit index for unknown ones */
182 std::ostringstream str;
183 bool comma = false;
184 if ( getNoncacheable() ) {
185 str << "F_NONCACHEABLE";
186 comma = true;
187 }
188 if ( getFail() ) {
189 if ( comma ) { str << ","; }
190 else {
191 comma = true;
192 }
193 str << "F_FAIL";
194 }
195 if ( getTrace() ) {
196 if ( comma ) { str << ","; }
197 else {
198 comma = true;
199 }
200 str << "F_TRACE";
201 }
202 for ( unsigned int i = 4; i < sizeof(flags_t); i++ ) {
203 flags_t shift = 1 << i;
204 if ( getFlag(shift) ) {
205 if ( comma ) { str << ","; }
206 else {
207 comma = true;
208 }
209 str << "F_" << i;
210 }
211 }
212 return str.str();
213 }
214
215 protected:
216 id_t id;
217 flags_t flags;
218
219 private:
220 static std::atomic<id_t> main_id;
221 };
222
223 /* Forward declarations */
224 class Read; /* Request to read data */
225 class ReadResp; /* Response to read requests */
226 class Write; /* Request to write data */
227 class WriteResp; /* Response to write requests */
228 class FlushAddr; /* Flush an address from cache */
229 class FlushResp; /* Response to flush request */
230 class ReadLock; /* Read and lock an address */
231 class WriteUnlock; /* Write and unlock an address */
232 class LoadLink; /* First part of LLSC, read and track access atomicity */
233 class StoreConditional; /* Second part of LLSC, check access atomicity and write if atomic */
234 class MoveData; /* Copy data from one address to another (e.g., Get/Put) */
235 class CustomReq; /* Encapsulates a custom class that defines some request event type */
236 class CustomResp; /* Encapsulates a custom class that defines some response event type */
237 class InvNotify; /* Notification that data has been invalidated */
238
239 /** Read request.
240 * Can be marked noncacheable to bypass caches.
241 * Response type is ReadResp
242 */
243 class Read : public Request
244 {
245 public:
246 Read(Addr physAddr, uint64_t size, flags_t flags = 0, Addr virtAddr = 0, Addr instPtr = 0, uint32_t tid = 0) :
247 Request(flags),
248 pAddr(physAddr),
249 vAddr(virtAddr),
250 size(size),
251 iPtr(instPtr),
252 tid(tid)
253 {}
254 virtual ~Read() {}
255
256 /** Create read response.
257 * User must manually set read data on response if simulation is using actual data values
258 * @return ReadResp formatted as a response to this Read request
259 */
261 {
262 std::vector<uint8_t> datavec(
263 size, 0); /* Placeholder. If actual data values are used in simulation, the model should update this */
264 ReadResp* resp = new ReadResp(this, datavec);
265 return resp;
266 }
267
268 bool needsResponse() override { return true; }
269
270 SST::Event* convert(RequestConverter* converter) override { return converter->convert(this); }
271
272 void handle(RequestHandler* handler) override { return handler->handle(this); }
273
274 std::string getString() override
275 {
276 std::ostringstream str;
277 str << "ID: " << id << ", Type: Read, Flags: [" << getFlagString() << "], PhysAddr: 0x" << std::hex << pAddr
278 << ", VirtAddr: 0x" << vAddr;
279 str << ", Size: " << std::dec << size << ", InstPtr: 0x" << std::hex << iPtr << ", ThreadID: " << std::dec
280 << tid;
281 return str.str();
282 }
283
284 /* Data members */
285 Addr pAddr; /* Physical address */
286 Addr vAddr; /* Virtual address */
287 uint64_t size; /* Number of bytes to read */
288 Addr iPtr; /* Instruction pointer - optional metadata */
289 uint32_t tid; /* Thread ID */
290 };
291
292 /** Response to a Read */
293 class ReadResp : public Request
294 {
295 public:
296 ReadResp(
297 id_t rid, Addr physAddr, uint64_t size, std::vector<uint8_t> respData, flags_t flags = 0, Addr virtAddr = 0,
298 Addr instPtr = 0, uint32_t tid = 0) :
299 Request(rid, flags),
300 pAddr(physAddr),
301 vAddr(virtAddr),
302 size(size),
303 data(respData),
304 iPtr(instPtr),
305 tid(tid)
306 {}
307
308 ReadResp(Read* readEv, std::vector<uint8_t> respData) :
309 Request(readEv->getID(), readEv->getAllFlags()),
310 pAddr(readEv->pAddr),
311 vAddr(readEv->vAddr),
312 size(readEv->size),
313 data(respData),
314 iPtr(readEv->iPtr),
315 tid(readEv->tid)
316 {}
317
318 virtual ~ReadResp() {}
319
320 Request* makeResponse() override { return nullptr; } /* No response type */
321
322 bool needsResponse() override { return false; }
323
324 SST::Event* convert(RequestConverter* converter) override { return converter->convert(this); }
325
326 void handle(RequestHandler* handler) override { return handler->handle(this); }
327
328 std::string getString() override
329 {
330 std::ostringstream str;
331 str << "ID: " << id << ", Type: ReadResp, Flags: [" << getFlagString() << "] PhysAddr: 0x" << std::hex
332 << pAddr;
333 str << ", VirtAddr: 0x" << vAddr << ", Size: " << std::dec << size << ", InstPtr: 0x" << std::hex << iPtr;
334 str << ", ThreadID: " << std::dec << tid << ", Payload: 0x" << std::hex;
335 str << std::setfill('0');
336 for ( std::vector<uint8_t>::iterator it = data.begin(); it != data.end(); it++ ) {
337 str << std::setw(2) << static_cast<unsigned>(*it);
338 }
339 return str.str();
340 }
341
342 /* Data members */
343 Addr pAddr; /* Physical address */
344 Addr vAddr; /* Virtual address */
345 uint64_t size; /* Number of bytes to read */
346 std::vector<uint8_t> data; /* Read data */
347 Addr iPtr; /* Instruction pointer - optional metadata */
348 uint32_t tid; /* Thread ID */
349 };
350
351 /** Request to write data.
352 * Can be marked noncacheable to bypass caches
353 * Response type is WriteResp
354 */
355 class Write : public Request
356 {
357 public:
358 /* Constructor */
359 Write(
360 Addr physAddr, uint64_t size, std::vector<uint8_t> wData, bool posted = false, flags_t flags = 0,
361 Addr virtAddr = 0, Addr instPtr = 0, uint32_t tid = 0) :
362 Request(flags),
363 pAddr(physAddr),
364 vAddr(virtAddr),
365 size(size),
366 data(wData),
367 posted(posted),
368 iPtr(instPtr),
369 tid(tid)
370 {}
371 /* Destructor */
372 virtual ~Write() {}
373
374 virtual Request* makeResponse() override { return new WriteResp(this); }
375
376 virtual bool needsResponse() override { return !posted; }
377
378 SST::Event* convert(RequestConverter* converter) override { return converter->convert(this); }
379
380 void handle(RequestHandler* handler) override { return handler->handle(this); }
381
382 std::string getString() override
383 {
384 std::ostringstream str;
385 str << "ID: " << id << ", Type: Write, Flags: [" << getFlagString() << "], PhysAddr: 0x" << std::hex
386 << pAddr;
387 str << ", VirtAddr: 0x" << vAddr << ", Size: " << std::dec << size << ", Posted: " << (posted ? "T" : "F");
388 str << ", InstPtr: 0x" << std::hex << iPtr << ", ThreadID: " << std::dec << tid << ", Payload: 0x"
389 << std::hex;
390 str << std::setfill('0');
391 for ( std::vector<uint8_t>::iterator it = data.begin(); it != data.end(); it++ ) {
392 str << std::setw(2) << static_cast<unsigned>(*it);
393 }
394 return str.str();
395 }
396
397 /* Data members */
398 Addr pAddr; /* Physical address */
399 Addr vAddr; /* Virtual address */
400 uint64_t size; /* Number of bytes to write */
401 std::vector<uint8_t> data; /* Written data */
402 bool posted; /* Whether write is posted (requires no response) */
403 Addr iPtr; /* Instruction pointer - optional metadata */
404 uint32_t tid; /* Thread ID */
405 };
406
407 /** Response to a Write */
408 class WriteResp : public Request
409 {
410 public:
411 /** Manually construct a write response */
413 id_t id, Addr physAddr, uint64_t size, flags_t flags = 0, Addr virtAddr = 0, Addr instPtr = 0,
414 uint32_t tid = 0) :
415 Request(id, flags),
416 pAddr(physAddr),
417 vAddr(virtAddr),
418 size(size),
419 iPtr(instPtr),
420 tid(tid)
421 {}
422 /** Automatically construct a write response from a Write */
424 Request(wr->getID(), wr->getAllFlags()),
425 pAddr(wr->pAddr),
426 vAddr(wr->vAddr),
427 size(wr->size),
428 iPtr(wr->iPtr),
429 tid(wr->tid)
430 {}
431 /** Destructor */
432 virtual ~WriteResp() {}
433
434 virtual Request* makeResponse() override { return nullptr; }
435
436 virtual bool needsResponse() override { return false; }
437
438 SST::Event* convert(RequestConverter* converter) override { return converter->convert(this); }
439
440 void handle(RequestHandler* handler) override { return handler->handle(this); }
441
442 std::string getString() override
443 {
444 std::ostringstream str;
445 str << "ID:" << id << ", Type: WriteResp, Flags: [" << getFlagString() << "], PhysAddr: 0x" << std::hex
446 << pAddr;
447 str << ", VirtAddr: 0x" << vAddr << ", Size: " << std::dec << size << ", InstPtr: 0x" << std::hex << iPtr;
448 str << ", ThreadID: " << std::dec << tid;
449 return str.str();
450 }
451
452 /* Data members */
453 Addr pAddr; /* Physical address */
454 Addr vAddr; /* Virtual address */
455 uint64_t size; /* Number of bytes to read */
456 Addr iPtr; /* Instruction pointer - optional metadata */
457 uint32_t tid; /* Thread ID */
458 };
459
460 /* Flush an address from cache
461 * Response type is FlushResp
462 * inv = false: Write back dirty data to memory, leave clean data in cache
463 * inv = true: Write back dirty data to memory, invalidate data in cache
464 */
465 class FlushAddr : public Request
466 {
467 public:
468 FlushAddr(
469 Addr physAddr, uint64_t size, bool inv, uint32_t depth, flags_t flags = 0, Addr virtAddr = 0,
470 Addr instPtr = 0, uint32_t tid = 0) :
471 Request(flags),
472 pAddr(physAddr),
473 vAddr(virtAddr),
474 size(size),
475 inv(inv),
476 depth(depth),
477 iPtr(instPtr),
478 tid(tid)
479 {}
480 virtual ~FlushAddr() {}
481
482 virtual Request* makeResponse() override { return new FlushResp(this); }
483
484 virtual bool needsResponse() override { return true; }
485
486 SST::Event* convert(RequestConverter* converter) override { return converter->convert(this); }
487
488 void handle(RequestHandler* handler) override { return handler->handle(this); }
489
490 std::string getString() override
491 {
492 std::ostringstream str;
493 str << "ID:" << id << ", Type: FlushAddr, Flags: [" << getFlagString() << "], PhysAddr: 0x" << std::hex
494 << pAddr;
495 str << ", VirtAddr: 0x" << vAddr << ", Size: " << std::dec << size << ", Inv: " << (inv ? "T" : "F");
496 str << ", Depth: " << depth << ", InstPtr: 0x" << std::hex << iPtr << ", ThreadID: " << std::dec << tid;
497 return str.str();
498 }
499
500 Addr pAddr; /* Physical address */
501 Addr vAddr; /* Virtual address */
502 uint64_t size; /* Number of bytes to invalidate */
503 bool inv; /* Whether flush should also invalidate line */
504 uint32_t depth; /* How many levels down the memory hierarchy this flush should propogate. E.g., 1 = L1 only, 2 =
505 L1 + L2, etc. */
506 Addr iPtr; /* Instruction pointer */
507 uint32_t tid; /* Thread ID */
508 };
509
510 /** Response to a flush request.
511 * Flushes can occasionally fail, check getSuccess() to determine success.
512 */
513 class FlushResp : public Request
514 {
515 public:
516 FlushResp(
517 id_t id, Addr physAddr, uint64_t size, flags_t flags = 0, Addr vAddr = 0, Addr instPtr = 0,
518 uint32_t tid = 0) :
519 Request(id, flags),
520 pAddr(physAddr),
521 vAddr(vAddr),
522 size(size),
523 iPtr(instPtr),
524 tid(tid)
525 {}
526 FlushResp(FlushAddr* fl, flags_t newFlags = 0) :
527 Request(fl->getID(), fl->getAllFlags() | newFlags),
528 pAddr(fl->pAddr),
529 vAddr(fl->vAddr),
530 size(fl->size),
531 iPtr(fl->iPtr),
532 tid(fl->tid)
533 {}
534 virtual ~FlushResp() {}
535
536 virtual Request* makeResponse() override { return nullptr; }
537
538 virtual bool needsResponse() override { return false; }
539
540 SST::Event* convert(RequestConverter* converter) override { return converter->convert(this); }
541
542 void handle(RequestHandler* handler) override { return handler->handle(this); }
543
544 std::string getString() override
545 {
546 std::ostringstream str;
547 str << "ID:" << id << ", Type: FlushResp, Flags: [" << getFlagString() << "], PhysAddr: 0x" << std::hex
548 << pAddr;
549 str << ", VirtAddr: 0x" << vAddr << ", Size: " << std::dec << size;
550 str << ", InstPtr: 0x" << std::hex << iPtr << ", ThreadID: " << std::dec << tid;
551 return str.str();
552 }
553
554 Addr pAddr; /* Physical address */
555 Addr vAddr; /* Virtual address */
556 uint64_t size; /* Number of bytes to invalidate */
557 Addr iPtr; /* Instruction pointer */
558 uint32_t tid; /* Thread ID */
559 };
560
561 /**
562 * Locked atomic update -> guaranteed success
563 * A ReadLock **must** be followed by a WriteUnlock
564 */
565
566 /** ReadLock acquires and locks an address
567 * Returns a ReadResp with the current data value
568 */
569 class ReadLock : public Request
570 {
571 public:
572 ReadLock(
573 Addr physAddr, uint64_t size, flags_t flags = 0, Addr virtAddr = 0, Addr instPtr = 0, uint32_t tid = 0) :
574 Request(flags),
575 pAddr(physAddr),
576 vAddr(virtAddr),
577 size(size),
578 iPtr(instPtr),
579 tid(tid)
580 {}
581 virtual ~ReadLock() {}
582
583 Request* makeResponse() override
584 {
585 std::vector<uint8_t> datavec(size, 0); /* This is a placeholder. If actual data values are used in
586 simulation, the model should update this */
587 return new ReadResp(id, pAddr, size, datavec, flags, vAddr, iPtr, tid);
588 }
589
590 bool needsResponse() override { return true; }
591
592 SST::Event* convert(RequestConverter* converter) override { return converter->convert(this); }
593
594 void handle(RequestHandler* handler) override { return handler->handle(this); }
595
596 std::string getString() override
597 {
598 std::ostringstream str;
599 str << "ID: " << id << ", Type: ReadLock, Flags: [" << getFlagString() << "] PhysAddr: 0x" << std::hex
600 << pAddr << ", VirtAddr: 0x" << vAddr;
601 str << ", Size: " << std::dec << size << ", InstPtr: 0x" << std::hex << iPtr << ", ThreadID: " << std::dec
602 << tid;
603 return str.str();
604 }
605
606 /* Data members */
607 Addr pAddr; /* Physical address */
608 Addr vAddr; /* Virtual address */
609 uint64_t size; /* Number of bytes to read */
610 Addr iPtr; /* Instruction pointer - optional metadata */
611 uint32_t tid; /* Thread ID */
612 };
613
614 /* WriteUnlock writes a locked address
615 * WriteUnlock will fatally error if lock is not acquired first
616 * Returns a WriteResp
617 */
618 class WriteUnlock : public Request
619 {
620 public:
622 Addr physAddr, uint64_t size, std::vector<uint8_t> wData, bool posted = false, flags_t flags = 0,
623 Addr virtAddr = 0, Addr instPtr = 0, uint32_t tid = 0) :
624 Request(flags),
625 pAddr(physAddr),
626 vAddr(virtAddr),
627 size(size),
628 data(wData),
629 posted(posted),
630 iPtr(instPtr),
631 tid(tid)
632 {}
633
634 virtual ~WriteUnlock() {}
635
636 virtual Request* makeResponse() override { return new WriteResp(id, pAddr, size, flags, vAddr = 0, iPtr, tid); }
637
638 virtual bool needsResponse() override { return !posted; }
639
640 SST::Event* convert(RequestConverter* converter) override { return converter->convert(this); }
641
642 void handle(RequestHandler* handler) override { return handler->handle(this); }
643
644 std::string getString() override
645 {
646 std::ostringstream str;
647 str << "ID: " << id << ", Type: WriteUnlock, Flags: [" << getFlagString() << "], PhysAddr: 0x" << std::hex
648 << pAddr;
649 str << ", VirtAddr: 0x" << vAddr << ", Size: " << std::dec << size << ", Posted: " << (posted ? "T" : "F");
650 str << ", InstPtr: 0x" << std::hex << iPtr << ", ThreadID: " << std::dec << tid << ", Payload: 0x"
651 << std::hex;
652 str << std::setfill('0');
653 for ( std::vector<uint8_t>::iterator it = data.begin(); it != data.end(); it++ ) {
654 str << std::setw(2) << static_cast<unsigned>(*it);
655 }
656 return str.str();
657 }
658
659 /* Data members */
660 Addr pAddr; /* Physical address */
661 Addr vAddr; /* Virtual address */
662 uint64_t size; /* Number of bytes to write */
663 std::vector<uint8_t> data; /* Written data */
664 bool posted; /* Whether write is posted (requires no response) */
665 Addr iPtr; /* Instruction pointer - optional metadata */
666 uint32_t tid; /* Thread ID */
667 };
668
669 /**
670 * Conditional atomic update. Can fail.
671 * A LoadLink should be followed by a StoreConditional
672 */
673
674 /* LoadLink loads an address and tracks it for atomicity
675 * Returns a ReadResp
676 */
677 class LoadLink : public Request
678 {
679 public:
680 LoadLink(
681 Addr physAddr, uint64_t size, flags_t flags = 0, Addr virtAddr = 0, Addr instPtr = 0, uint32_t tid = 0) :
682 Request(flags),
683 pAddr(physAddr),
684 vAddr(virtAddr),
685 size(size),
686 iPtr(instPtr),
687 tid(tid)
688 {}
689 virtual ~LoadLink() {}
690
691 Request* makeResponse() override
692 {
693 std::vector<uint8_t> datavec(size, 0); /* This is a placeholder. If actual data values are used in
694 simulation, the model should update this */
695 return new ReadResp(id, pAddr, size, datavec, flags, vAddr, iPtr, tid);
696 }
697
698 bool needsResponse() override { return true; }
699
700 SST::Event* convert(RequestConverter* converter) override { return converter->convert(this); }
701
702 void handle(RequestHandler* handler) override { return handler->handle(this); }
703
704 std::string getString() override
705 {
706 std::ostringstream str;
707 str << "ID: " << id << ", Type: LoadLink, Flags: [" << getFlagString() << "] PhysAddr: 0x" << std::hex
708 << pAddr << ", VirtAddr: 0x" << vAddr;
709 str << ", Size: " << std::dec << size << ", InstPtr: 0x" << std::hex << iPtr << ", ThreadID: " << std::dec
710 << tid;
711 return str.str();
712 }
713
714 /* Data members */
715 Addr pAddr; /* Physical address */
716 Addr vAddr; /* Virtual address */
717 uint64_t size; /* Number of bytes to read */
718 Addr iPtr; /* Instruction pointer - optional metadata */
719 uint32_t tid; /* Thread ID */
720 };
721
722 /* StoreConditional checks if a write to a prior LoadLink address will be atomic,
723 * if so, writes the address and returns a WriteResp with getSuccess() == true
724 * if not, does not write the address and returns a WriteResp with getSuccess() == false
725 */
727 {
728 public:
730 Addr physAddr, uint64_t size, std::vector<uint8_t> wData, flags_t flags = 0, Addr virtAddr = 0,
731 Addr instPtr = 0, uint32_t tid = 0) :
732 Request(flags),
733 pAddr(physAddr),
734 vAddr(virtAddr),
735 size(size),
736 data(wData),
737 iPtr(instPtr),
738 tid(tid)
739 {}
740
741 virtual ~StoreConditional() {}
742
743 /* Model must also call setFail() on response if LLSC failed */
744 virtual Request* makeResponse() override { return new WriteResp(id, pAddr, size, flags, vAddr, iPtr, tid); }
745
746 virtual bool needsResponse() override { return true; }
747
748 SST::Event* convert(RequestConverter* converter) override { return converter->convert(this); }
749
750 void handle(RequestHandler* handler) override { return handler->handle(this); }
751
752 std::string getString() override
753 {
754 std::ostringstream str;
755 str << "ID: " << id << ", Type: StoreConditional, Flags: [" << getFlagString() << "], PhysAddr: 0x"
756 << std::hex << pAddr;
757 str << ", VirtAddr: 0x" << vAddr << ", Size: " << std::dec << size;
758 str << ", InstPtr: 0x" << std::hex << iPtr << ", ThreadID: " << std::dec << tid << ", Payload: 0x"
759 << std::hex;
760 str << std::setfill('0');
761 for ( std::vector<uint8_t>::iterator it = data.begin(); it != data.end(); it++ ) {
762 str << std::setw(2) << static_cast<unsigned>(*it);
763 }
764 return str.str();
765 }
766
767 /* Data members */
768 Addr pAddr; /* Physical address */
769 Addr vAddr; /* Virtual address */
770 uint64_t size; /* Number of bytes to write */
771 std::vector<uint8_t> data; /* Written data */
772 Addr iPtr; /* Instruction pointer - optional metadata */
773 uint32_t tid; /* Thread ID */
774 };
775
776 /* Explicit data movement */
777 /** Move: move data from one address to another
778 * Returns a WriteResp
779 */
780 class MoveData : public Request
781 {
782 public:
783 MoveData(
784 Addr pSrc, Addr pDst, uint64_t size, bool posted = false, flags_t flags = 0, Addr vSrc = 0, Addr vDst = 0,
785 Addr iPtr = 0, uint32_t tid = 0) :
786 Request(flags),
787 pSrc(pSrc),
788 vSrc(vSrc),
789 pDst(pDst),
790 vDst(vDst),
791 size(size),
792 posted(posted),
793 iPtr(iPtr),
794 tid(tid)
795 {}
796 virtual ~MoveData() {}
797
798 virtual Request* makeResponse() override { return new WriteResp(id, pDst, size, flags, vDst, iPtr, tid); }
799
800 virtual bool needsResponse() override { return !posted; }
801
802 SST::Event* convert(RequestConverter* converter) override { return converter->convert(this); }
803
804 void handle(RequestHandler* handler) override { return handler->handle(this); }
805
806 std::string getString() override
807 {
808 std::ostringstream str;
809 str << "ID: " << id << ", Type: MoveData, Flags: [" << getFlagString() << "], SrcPhysAddr: 0x" << std::hex
810 << pSrc;
811 str << ", SrcVirtAddr: 0x" << vSrc << ", DstPhysAddr: 0x" << pDst << ", DstVirtAddr: 0x" << vDst;
812 str << ", Size: " << std::dec << size << ", Posted: " << (posted ? "T" : "F");
813 str << ", InstPtr: 0x" << std::hex << iPtr << ", ThreadID: " << std::dec << tid;
814 return str.str();
815 }
816
817 /* Data members */
818 Addr pSrc; /* Physical address of source */
819 Addr vSrc; /* Virtual address of source */
820 Addr pDst; /* Physical address of destination */
821 Addr vDst; /* Virtual address of destination */
822 uint64_t size; /* Number of bytes to move */
823 bool posted; /* True if a response is needed */
824 Addr iPtr; /* Instruction pointer */
825 uint32_t tid; /* Thread ID */
826 };
827
828 /** Notifies endpoint that an address has been invalidated from the L1.
829 */
830 class InvNotify : public Request
831 {
832 public:
833 InvNotify(Addr pAddr, uint64_t size, flags_t flags = 0, Addr vAddr = 0, Addr iPtr = 0, uint32_t tid = 0) :
834 Request(flags),
835 pAddr(pAddr),
836 vAddr(vAddr),
837 size(size),
838 iPtr(iPtr),
839 tid(tid)
840 {}
841 virtual ~InvNotify() {}
842
843 virtual Request* makeResponse() override { return nullptr; }
844
845 virtual bool needsResponse() override { return false; }
846
847 SST::Event* convert(RequestConverter* converter) override { return converter->convert(this); }
848
849 void handle(RequestHandler* handler) override { return handler->handle(this); }
850
851 std::string getString() override
852 {
853 std::ostringstream str;
854 str << "ID: " << id << ", Type: InvNotify, Flags: [" << getFlagString() << "], PhysAddr: 0x" << std::hex
855 << pAddr;
856 str << ", VirtAddr: 0x" << vAddr << ", Size: " << std::dec << size;
857 str << ", InstPtr: 0x" << std::hex << iPtr << ", ThreadID: " << std::dec << tid;
858 return str.str();
859 }
860
861 Addr pAddr; /* Physical address */
862 Addr vAddr; /* Virtual address */
863 uint64_t size; /* Number of bytes invalidated */
864 Addr iPtr; /* Instruction pointer */
865 uint32_t tid; /* Thread ID */
866 };
867
868 /* This class can be inherited to create custom events that can be handled in a limited fashion by existing
869 * interfaces Child class must be serializable
870 */
872 {
873 public:
874 CustomData() {}
875 virtual ~CustomData() {}
876 virtual Addr getRoutingAddress() = 0; /* Return address to use for routing this event to its destination */
877 virtual uint64_t getSize() = 0; /* Return size to use when accounting for bandwidth used is needed */
878 virtual CustomData* makeResponse() = 0; /* Return a CustomData* object formatted as a response */
879 virtual bool needsResponse() = 0; /* Return whether a response is needed */
880 virtual std::string getString() = 0; /* String representation for debug/output/etc. */
881
882 /* This needs to be serializable so that we can use it in events in parallel simulations */
883 virtual void serialize_order(SST::Core::Serialization::serializer& UNUSED(ser)) override = 0;
884 // ImplementSerializable(SST::Interfaces::StandardMem::CustomData);
885 ImplementVirtualSerializable(CustomData);
886 };
887
888 class CustomReq : public Request
889 {
890 public:
891 CustomReq(CustomData* data, flags_t flags = 0, Addr iPtr = 0, uint32_t tid = 0) :
892 Request(flags),
893 data(data),
894 iPtr(iPtr),
895 tid(tid)
896 {}
897 virtual ~CustomReq() {}
898
899 virtual Request* makeResponse() override { return new CustomResp(this); }
900
901 virtual bool needsResponse() override { return data->needsResponse(); }
902
903 SST::Event* convert(RequestConverter* converter) override { return converter->convert(this); }
904
905 void handle(RequestHandler* handler) override { return handler->handle(this); }
906
907 std::string getString() override
908 {
909 std::ostringstream str;
910 str << "ID: " << id << ", Type: CustomReq, Flags: [" << getFlagString() << "], " << data->getString();
911 str << ", InstPtr: 0x" << std::hex << iPtr << ", ThreadID: " << std::dec << tid;
912 return str.str();
913 }
914
915 CustomData* data; /* Custom class that holds data for this event */
916 Addr iPtr; /* Instruction pointer */
917 uint32_t tid; /* Thread ID */
918 };
919
920 class CustomResp : public Request
921 {
922 public:
923 CustomResp(id_t id, CustomData* data, flags_t flags = 0, Addr iPtr = 0, uint32_t tid = 0) :
924 Request(id, flags),
925 data(data),
926 iPtr(iPtr),
927 tid(tid)
928 {}
929 CustomResp(CustomReq* req) :
930 Request(req->getID(), req->getAllFlags()),
931 data(req->data->makeResponse()),
932 iPtr(req->iPtr),
933 tid(req->tid)
934 {}
935 virtual ~CustomResp() {}
936
937 virtual Request* makeResponse() override { return nullptr; }
938
939 virtual bool needsResponse() override { return false; }
940
941 SST::Event* convert(RequestConverter* converter) override { return converter->convert(this); }
942
943 void handle(RequestHandler* handler) override { return handler->handle(this); }
944
945 std::string getString() override
946 {
947 std::ostringstream str;
948 str << "ID: " << id << ", Type: CustomResp, Flags: [" << getFlagString() << "], " << data->getString();
949 str << ", InstPtr: 0x" << std::hex << iPtr << ", ThreadID: " << std::dec << tid;
950 return str.str();
951 }
952
953 CustomData* data; /* Custom class that holds data for this event */
954 Addr iPtr; /* Instruction pointer */
955 uint32_t tid; /* Thread ID */
956 };
957
958 /* Class for implementation-specific converter functions */
960 {
961 public:
963 virtual ~RequestConverter() {}
964
965 /* Built in command converters */
966 virtual SST::Event* convert(Read* request) = 0;
967 virtual SST::Event* convert(ReadResp* request) = 0;
968 virtual SST::Event* convert(Write* request) = 0;
969 virtual SST::Event* convert(WriteResp* request) = 0;
970 virtual SST::Event* convert(FlushAddr* request) = 0;
971 virtual SST::Event* convert(FlushResp* request) = 0;
972 virtual SST::Event* convert(ReadLock* request) = 0;
973 virtual SST::Event* convert(WriteUnlock* request) = 0;
974 virtual SST::Event* convert(LoadLink* request) = 0;
975 virtual SST::Event* convert(StoreConditional* request) = 0;
976 virtual SST::Event* convert(MoveData* request) = 0;
977 virtual SST::Event* convert(CustomReq* request) = 0;
978 virtual SST::Event* convert(CustomResp* request) = 0;
979 virtual SST::Event* convert(InvNotify* request) = 0;
980 };
981
982 /* Class for implementation-specific handler functions */
984 {
985 public:
986 RequestHandler(SST::Output* o) : out(o) {}
987 virtual ~RequestHandler() {}
988
989 /* Built in command handlers */
990 virtual void handle(Read* UNUSED(request))
991 {
992 out->fatal(CALL_INFO, -1, "Error: RequestHandler for Read requests is not implemented\n");
993 }
994 virtual void handle(ReadResp* UNUSED(request))
995 {
996 out->fatal(CALL_INFO, -1, "Error: RequestHandler for ReadResp requests is not implemented\n");
997 }
998 virtual void handle(Write* UNUSED(request))
999 {
1000 out->fatal(CALL_INFO, -1, "Error: RequestHandler for Write requests is not implemented\n");
1001 }
1002 virtual void handle(WriteResp* UNUSED(request))
1003 {
1004 out->fatal(CALL_INFO, -1, "Error: RequestHandler for WriteResp requests is not implemented\n");
1005 }
1006 virtual void handle(FlushAddr* UNUSED(request))
1007 {
1008 out->fatal(CALL_INFO, -1, "Error: RequestHandler for FlushAddr requests is not implemented\n");
1009 }
1010 virtual void handle(FlushResp* UNUSED(request))
1011 {
1012 out->fatal(CALL_INFO, -1, "Error: RequestHandler for FlushResp requests is not implemented\n");
1013 }
1014 virtual void handle(ReadLock* UNUSED(request))
1015 {
1016 out->fatal(CALL_INFO, -1, "Error: RequestHandler for ReadLock requests is not implemented\n");
1017 }
1018 virtual void handle(WriteUnlock* UNUSED(request))
1019 {
1020 out->fatal(CALL_INFO, -1, "Error: RequestHandler for WriteUnlock requests is not implemented\n");
1021 }
1022 virtual void handle(LoadLink* UNUSED(request))
1023 {
1024 out->fatal(CALL_INFO, -1, "Error: RequestHandler for LoadLink requests is not implemented\n");
1025 }
1026 virtual void handle(StoreConditional* UNUSED(request))
1027 {
1028 out->fatal(CALL_INFO, -1, "Error: RequestHandler for StoreConditional requests is not implemented\n");
1029 }
1030 virtual void handle(MoveData* UNUSED(request))
1031 {
1032 out->fatal(CALL_INFO, -1, "Error: RequestHandler for MoveData requests is not implemented\n");
1033 }
1034 virtual void handle(CustomReq* UNUSED(request))
1035 {
1036 out->fatal(CALL_INFO, -1, "Error: RequestHandler for CustomReq requests is not implemented\n");
1037 }
1038 virtual void handle(CustomResp* UNUSED(request))
1039 {
1040 out->fatal(CALL_INFO, -1, "Error: RequestHandler for CustomResp requests is not implemented\n");
1041 }
1042 virtual void handle(InvNotify* UNUSED(request))
1043 {
1044 out->fatal(CALL_INFO, -1, "Error: RequestHandler for InvNotify requests is not implemented\n");
1045 }
1046
1047 SST::Output* out;
1048 };
1049
1050 /** Constructor, designed to be used via 'loadUserSubComponent' and 'loadAnonymousSubComponent'.
1051 *
1052 * @param id Component ID assigned to this subcomponent
1053 * @param params Parameters passed to this subcomponent
1054 * @param time TimeConverter indicating the time base (e.g., clock period) associated with this endpoint
1055 * @param handler Callback function to use for event receives
1056 */
1058 SST::ComponentId_t id, Params& UNUSED(params), TimeConverter*& UNUSED(time), HandlerBase*& UNUSED(handler)) :
1059 SubComponent(id)
1060 {}
1061
1062 /**
1063 * Sends a memory-based request during the init()/complete() phases
1064 * @param req Request to send
1065 */
1066 virtual void sendUntimedData(Request* req) = 0;
1067
1068 /**
1069 * Receive any data during the init()/complete() phases.
1070 * @see SST::Link::recvInitData()
1071 * Handler is not used during init()/complete(), parent must
1072 * poll this interface to get received events.
1073 *
1074 * @return Event if one was received, otherwise nullptr
1075 */
1076 virtual Request* recvUntimedData() = 0;
1077
1078 /**
1079 * Send a Request through the interface
1080 *
1081 * @param req Request to send
1082 */
1083 virtual void send(Request* req) = 0;
1084
1085 /**
1086 * Receive a Request response from the side of the link.
1087 *
1088 * Use this method for polling-based applications.
1089 * Register a handler for push-based notification of responses.
1090 *
1091 * @return nullptr if nothing is available.
1092 * @return Pointer to a Request response
1093 * Upon receipt, the receiver takes responsibility for deleting the event
1094 */
1095 virtual Request* poll(void) = 0;
1096
1097 /**
1098 * Get cache/memory line size (in bytes) from the memory system
1099 *
1100 * The memory system should provide this and it should be
1101 * valid after the init() phase is complete, so processors
1102 * can safely call this function during setup().
1103 *
1104 * @return 0 if the interface does not provide this capability or not relevant
1105 * @return line size of the memory system
1106 */
1107 virtual Addr getLineSize() = 0;
1108
1109 /**
1110 * Sets the physical memory address(es), if any, that are mapped
1111 * to this endpoint. Not required for endpoints that are not mapped
1112 * into the memory address space.
1113 *
1114 * Components loading this subcomponent as an MMIO device must call this
1115 * function prior to SST's init() phase.
1116 *
1117 * @param start Base address of the region mapped to this endpoint
1118 * @param size Size, in bytes, of the region mapped to this endpoint
1119 */
1120 virtual void setMemoryMappedAddressRegion(Addr start, Addr size) = 0;
1121};
1122
1123} // namespace Interfaces
1124} // namespace SST
1125
1126#endif // SST_CORE_INTERFACES_STANDARDMEM_H
Definition: serializable.h:119
This class is basically a wrapper for objects to declare the order in which their members should be s...
Definition: serializer.h:35
Base class for Events - Items sent across links to communicate between components.
Definition: event.h:35
Response to a flush request.
Definition: stdMem.h:514
Notifies endpoint that an address has been invalidated from the L1.
Definition: stdMem.h:831
Move: move data from one address to another Returns a WriteResp.
Definition: stdMem.h:781
Locked atomic update -> guaranteed success A ReadLock must be followed by a WriteUnlock.
Definition: stdMem.h:570
Response to a Read.
Definition: stdMem.h:294
Read request.
Definition: stdMem.h:244
Request * makeResponse() override
Create read response.
Definition: stdMem.h:260
Base class for StandardMem commands.
Definition: stdMem.h:111
Flag
Flags that modify requests.
Definition: stdMem.h:121
Response to a Write.
Definition: stdMem.h:409
WriteResp(id_t id, Addr physAddr, uint64_t size, flags_t flags=0, Addr virtAddr=0, Addr instPtr=0, uint32_t tid=0)
Manually construct a write response.
Definition: stdMem.h:412
WriteResp(Write *wr)
Automatically construct a write response from a Write.
Definition: stdMem.h:423
virtual ~WriteResp()
Destructor.
Definition: stdMem.h:432
Request to write data.
Definition: stdMem.h:356
Generic interface to Memory models.
Definition: stdMem.h:70
virtual Request * recvUntimedData()=0
Receive any data during the init()/complete() phases.
StandardMem(SST::ComponentId_t id, Params &UNUSED(params), TimeConverter *&UNUSED(time), HandlerBase *&UNUSED(handler))
Constructor, designed to be used via 'loadUserSubComponent' and 'loadAnonymousSubComponent'.
Definition: stdMem.h:1057
virtual Request * poll(void)=0
Receive a Request response from the side of the link.
virtual Addr getLineSize()=0
Get cache/memory line size (in bytes) from the memory system.
virtual void sendUntimedData(Request *req)=0
Sends a memory-based request during the init()/complete() phases.
virtual void setMemoryMappedAddressRegion(Addr start, Addr size)=0
Sets the physical memory address(es), if any, that are mapped to this endpoint.
virtual void send(Request *req)=0
Send a Request through the interface.
uint64_t Addr
All Addresses can be 64-bit.
Definition: stdMem.h:105
Output object provides consistent method for outputting data to stdout, stderr and/or sst debug file.
Definition: output.h:52
void fatal(uint32_t line, const char *file, const char *func, int exit_code, const char *format,...) const
Output the fatal message with formatting as specified by the format parameter.
Definition: output.cc:155
Parameter store.
Definition: params.h:56
Handlers with 1 handler defined argument to callback from caller.
Definition: ssthandler.h:171
Handler class with user-data argument.
Definition: ssthandler.h:220
SubComponent is a class loadable through the factory which allows dynamic functionality to be added t...
Definition: subcomponent.h:29
A class to convert between a component's view of time and the core's view of time.
Definition: timeConverter.h:27