Memory

The SimEng core models simulate everything in a core up to and including the load/store units, but stop short of the memory system. This is to allow integration and interoperability with a wide range of external memory models, and to focus the development of SimEng primarily on the simulation of the core itself.

MemoryInterface

All SimEng components that interact with the memory system make memory access requests using supplied instances of the abstract MemoryInterface class.

MemoryInterface access requests are asynchronous, and may be either read or write. All requests must supply a MemoryAccessTarget, containing the memory address and the number of bytes to access

While write requests receive no response, a read request may be responded to an indeterminate number of cycles later. The MemoryInterface::getCompletedReads function may be used to retrieve a list of the read requests that completed during the previous cycle. Once processed, responses should be dismissed using the MemoryInterface::clearCompletedReads function.

Note

Future versions may update the interface to remove the need for the component to manually clear completed reads.

In addition to the MemoryAccessTarget, a write request must be supplied with the data to be stored via a RegisterValue class instance, and a read request must be supplied with a unique identifier via a uint64_t value.

It is expected that all implementations of MemoryInterface should respect the order that requests are made: a read request following a write request to the same address should respond with the newly written value, rather than returning the old, stale result.

FlatMemoryInterface

For simpler models, a FlatMemoryInterface implementation is supplied. This is a simple wrapper around a byte array representing the process memory, and will always respond to all requests instantly and synchronously.

FixedMemoryInterface

For more complex models, a FixedMemoryInterface implementation is supplied. Similar to the FlatMemoryInterface, a simple wrapper around a byte array is used to represent the process memory. However, a pendingRequests_ queue is utilised in combination with an internal clock, tickCounter_, to support memory requests with a predefined fixed latency value named latency_.

A MemoryAccessTarget is transformed into a FixedLatencyMemoryInterfaceRequest when pushed onto the pendingRequests_ queue. Each FixedLatencyMemoryInterfaceRequest contains the original MemoryAccessTarget, an optional data or requestId value to hold a write’s RegisterValue or read’s unique id respectively, and a readyAt value. The readyAt value defines when the request is ready to be performed in relation to the tickCounter_, with readyAt = tickCounter_ + latency_ at the time of the initial request. When tickCounter_ is equivalent to the readyAt value, the request is performed.