Difference between revisions of "Memory System"
Line 1: | Line 1: | ||
M5's new memory system was redesigned with the following goals. | M5's new memory system was redesigned with the following goals. | ||
− | # Combine the timing and functional accesses into one. With the old memory system the timing accesses did not have data and just accounted for the time it would take to do an operation. Then the functional access actually made the operation | + | # Combine the timing and functional accesses into one. With the old memory system the timing accesses did not have data and just accounted for the time it would take to do an operation. Then the functional access actually made the operation visible to the system. This method was confusing, it allowed simulated components to accidently cheat, and wasn't reasonable for an execute-in-execute CPU model. |
# Simplify the memory system code -- remove the huge amount of templating and duplicate code. | # Simplify the memory system code -- remove the huge amount of templating and duplicate code. | ||
− | # Make changes easier. Specifically to allow other memory | + | # Make changes easier. Specifically to allow other memory hierarchies other than a shared bus based memory system. |
===MemObjects=== | ===MemObjects=== | ||
− | All objects that connect to the memory system inherit from <code> | + | All objects that connect to the memory system inherit from <code>MemObject</code>. This class adds the pure virtual function <code>getPort(const std::string &name)</code>which returns a port corresponding to the given name. This interface is used to connect memory objects together with the help of a connector (see below). |
=== Packet === | === Packet === | ||
Line 12: | Line 12: | ||
===Ports=== | ===Ports=== | ||
− | The next large part of the memory system in the idea of ports. Ports are used to interface memory objects to each other. They will always come in pairs and we refer to the | + | The next large part of the memory system in the idea of ports. Ports are used to interface memory objects to each other. They will always come in pairs and we refer to the other port object as the peer. These are used to make the design more modular. With ports a specific interface between every type of object doesn't have to be created. Every memory object has to have at least one port to be useful. |
There are two groups of functions in the port object. The send* functions are called on the port by the object that owns that port. For example to send a packet in the memory system a CPU would call <code>myPort->sendTiming(pkt)</code> to send a packet. Each send function has a corresponding recv function that is called on the ports peer. So the implementation of the <code>sendTiming()</code> call above would simply be <code> peer->recvTiming(pkt)</code>. Using this method we only have one virtual function call penalty but keep generic ports that can connect together any memory system objects. | There are two groups of functions in the port object. The send* functions are called on the port by the object that owns that port. For example to send a packet in the memory system a CPU would call <code>myPort->sendTiming(pkt)</code> to send a packet. Each send function has a corresponding recv function that is called on the ports peer. So the implementation of the <code>sendTiming()</code> call above would simply be <code> peer->recvTiming(pkt)</code>. Using this method we only have one virtual function call penalty but keep generic ports that can connect together any memory system objects. | ||
Line 21: | Line 21: | ||
=== Access Types=== | === Access Types=== | ||
There are three types of accesses supported by the ports. | There are three types of accesses supported by the ports. | ||
− | # '''Timing''' - Timing accesses are the most detailed access. They reflect our best effort for realistic timing and include the | + | # '''Timing''' - Timing accesses are the most detailed access. They reflect our best effort for realistic timing and include the modeling of queuing delay and resource contention. Once a timing request is successfully sent at some point in the future the device that sent the request will either get the response or a NACK if the request could not be completed (more below). Timing and Atomic accesses can not coexist in the memory system. |
− | # '''Atomic''' - | + | # '''Atomic''' - Atomic accesses are a faster lest detailed access. They are used for fast forwarding and warming up caches and return an approximate time to complete the request without any resource contention or queuing delay. When a atomic access is sent the response is provided when the function returns. Atomic and timing accesses can not coexist in the memory system. |
− | # '''Functional''' -- Like atomic accesses functional accesses happen | + | # '''Functional''' -- Like atomic accesses functional accesses happen instantaneously, but unlike atomic accesses they can coexist in the memory system with atomic on timing accesses. Functional accesses are used for things such as loading binaries, examining/changing variables in the simulated system, and allowing a remote debugger to be attached to the simulator. The important note is when a functional access is received by a device, if it contains a queue of packets all the packets must be searched for requests or responses that the functional access is effecting and they must be updated as appropriate. The <code>Packet::intersect()</code> and <code>fixPacket()</code> methods can help with this. |
=== Timing Flow control === | === Timing Flow control === | ||
=== Response and Snoop ranges === | === Response and Snoop ranges === | ||
+ | |||
+ | === Port Descendants === |
Revision as of 13:17, 8 June 2006
M5's new memory system was redesigned with the following goals.
- Combine the timing and functional accesses into one. With the old memory system the timing accesses did not have data and just accounted for the time it would take to do an operation. Then the functional access actually made the operation visible to the system. This method was confusing, it allowed simulated components to accidently cheat, and wasn't reasonable for an execute-in-execute CPU model.
- Simplify the memory system code -- remove the huge amount of templating and duplicate code.
- Make changes easier. Specifically to allow other memory hierarchies other than a shared bus based memory system.
Contents
MemObjects
All objects that connect to the memory system inherit from MemObject
. This class adds the pure virtual function getPort(const std::string &name)
which returns a port corresponding to the given name. This interface is used to connect memory objects together with the help of a connector (see below).
Packet
Request
Ports
The next large part of the memory system in the idea of ports. Ports are used to interface memory objects to each other. They will always come in pairs and we refer to the other port object as the peer. These are used to make the design more modular. With ports a specific interface between every type of object doesn't have to be created. Every memory object has to have at least one port to be useful.
There are two groups of functions in the port object. The send* functions are called on the port by the object that owns that port. For example to send a packet in the memory system a CPU would call myPort->sendTiming(pkt)
to send a packet. Each send function has a corresponding recv function that is called on the ports peer. So the implementation of the sendTiming()
call above would simply be peer->recvTiming(pkt)
. Using this method we only have one virtual function call penalty but keep generic ports that can connect together any memory system objects.
Connectors
To connect ports together to make a useable configuration connector objects are used. These simple connector objects take two memory objects and port names as parameters and simply called p1 = obj1->getPort(); p2 = obj2->getPort(); p1->setPeer(p2); p2->setPeer(p1);
(In reality more error checking is done). This happens during the connection phase of startup after all simobjects have been created.
Access Types
There are three types of accesses supported by the ports.
- Timing - Timing accesses are the most detailed access. They reflect our best effort for realistic timing and include the modeling of queuing delay and resource contention. Once a timing request is successfully sent at some point in the future the device that sent the request will either get the response or a NACK if the request could not be completed (more below). Timing and Atomic accesses can not coexist in the memory system.
- Atomic - Atomic accesses are a faster lest detailed access. They are used for fast forwarding and warming up caches and return an approximate time to complete the request without any resource contention or queuing delay. When a atomic access is sent the response is provided when the function returns. Atomic and timing accesses can not coexist in the memory system.
- Functional -- Like atomic accesses functional accesses happen instantaneously, but unlike atomic accesses they can coexist in the memory system with atomic on timing accesses. Functional accesses are used for things such as loading binaries, examining/changing variables in the simulated system, and allowing a remote debugger to be attached to the simulator. The important note is when a functional access is received by a device, if it contains a queue of packets all the packets must be searched for requests or responses that the functional access is effecting and they must be updated as appropriate. The
Packet::intersect()
andfixPacket()
methods can help with this.