SST  13.0.0
StructuralSimulationToolkit
stdMem.h
1 // -*- mode: c++ -*-
2 // Copyright 2009-2023 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-2023, 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 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  */
69 class StandardMem : public SubComponent
70 {
71 public:
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  */
110  class Request
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)) == 0; }
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  */
260  Request* makeResponse() override
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:
621  WriteUnlock(
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  */
726  class StoreConditional : public Request
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:
962  RequestConverter() {}
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
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
Handlers with 1 handler defined argument to callback from caller.
Definition: ssthandler.h:170
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:159
Request * makeResponse() override
Create read response.
Definition: stdMem.h:260
Response to a Write.
Definition: stdMem.h:408
virtual Request * poll(void)=0
Receive a Request response from the side of the link.
Handler class with user-data argument.
Definition: ssthandler.h:219
A class to convert between a component&#39;s view of time and the core&#39;s view of time.
Definition: timeConverter.h:26
Base class for StandardMem commands.
Definition: stdMem.h:110
Response to a Read.
Definition: stdMem.h:293
Definition: action.cc:18
virtual ~WriteResp()
Destructor.
Definition: stdMem.h:432
Move: move data from one address to another Returns a WriteResp.
Definition: stdMem.h:780
Generic interface to Memory models.
Definition: stdMem.h:69
Definition: serializable.h:118
virtual void setMemoryMappedAddressRegion(Addr start, Addr size)=0
Sets the physical memory address(es), if any, that are mapped to this endpoint.
Locked atomic update -> guaranteed success A ReadLock must be followed by a WriteUnlock.
Definition: stdMem.h:569
virtual Request * recvUntimedData()=0
Receive any data during the init()/complete() phases.
Response to a flush request.
Definition: stdMem.h:513
Flag
Flags that modify requests.
Definition: stdMem.h:121
Read request.
Definition: stdMem.h:243
Notifies endpoint that an address has been invalidated from the L1.
Definition: stdMem.h:830
uint64_t Addr
All Addresses can be 64-bit.
Definition: stdMem.h:100
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
Parameter store.
Definition: params.h:55
virtual Addr getLineSize()=0
Get cache/memory line size (in bytes) from the memory system.
virtual void send(Request *req)=0
Send a Request through the interface.
virtual void sendUntimedData(Request *req)=0
Sends a memory-based request 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 &#39;loadUserSubComponent&#39; and &#39;loadAnonymousSubComponent&#39;.
Definition: stdMem.h:1057
Base class for Events - Items sent across links to communicate between components.
Definition: event.h:34
SubComponent is a class loadable through the factory which allows dynamic functionality to be added t...
Definition: subcomponent.h:28
Request to write data.
Definition: stdMem.h:355
WriteResp(Write *wr)
Automatically construct a write response from a Write.
Definition: stdMem.h:423