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