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