| <!DOCTYPE html> |
| <html> |
| <head> |
| <meta charset="utf-8"> |
| <meta http-equiv="x-ua-compatible" content="ie=edge"> |
| <meta name="viewport" content="width=device-width, initial-scale=1"> |
| |
| <title>gem5</title> |
| |
| <!-- SITE FAVICON --> |
| <link rel="shortcut icon" type="image/gif" href="/assets/img/gem5ColorVert.gif"/> |
| |
| <link rel="canonical" href="http://localhost:4000/directory/"> |
| <link href='https://fonts.googleapis.com/css?family=Open+Sans:400,300,700,800,600' rel='stylesheet' type='text/css'> |
| <link href='https://fonts.googleapis.com/css?family=Muli:400,300' rel='stylesheet' type='text/css'> |
| |
| <!-- FAVICON --> |
| <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css"> |
| |
| <!-- BOOTSTRAP --> |
| <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous"> |
| |
| <!-- CUSTOM CSS --> |
| <link rel="stylesheet" href="/css/main.css"> |
| </head> |
| |
| |
| <body> |
| <nav class="navbar navbar-expand-md navbar-light bg-light"> |
| <a class="navbar-brand" href="/"> |
| <img src="/assets/img/gem5ColorLong.gif" alt="gem5" height=45px> |
| </a> |
| <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavDropdown" aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation"> |
| <span class="navbar-toggler-icon"></span> |
| </button> |
| <div class="collapse navbar-collapse" id="navbarNavDropdown"> |
| <ul class="navbar-nav ml-auto"> |
| <li class="nav-item "> |
| <a class="nav-link" href="/">Home</a> |
| </li> |
| |
| <li class="nav-item dropdown "> |
| <a class="nav-link dropdown-toggle" href="/about" id="navbarDropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> |
| About |
| </a> |
| <div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink"> |
| <a class="dropdown-item" href="/about">About</a> |
| <a class="dropdown-item" href="/publications">Publications</a> |
| <a class="dropdown-item" href="/governance">Governance</a> |
| </div> |
| </li> |
| |
| <li class="nav-item dropdown active"> |
| <a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> |
| Documentation |
| </a> |
| <div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink"> |
| <!-- Pull navigation from _data/documentation.yml --> |
| |
| <a class="dropdown-item" href="/introduction">Introduction</a> |
| |
| <a class="dropdown-item" href="/building">Getting Started</a> |
| |
| <a class="dropdown-item" href="/environment">Modifying/Extending</a> |
| |
| <a class="dropdown-item" href="/MSIintro">Modeling Cache Coherence with Ruby</a> |
| |
| </div> |
| </li> |
| |
| <li class="nav-item "> |
| <a class="nav-link" href="/contributing">Contributing</a> |
| </li> |
| |
| <li class="nav-item "> |
| <a class="nav-link" href="/blog">Blog</a> |
| </li> |
| |
| <li class="nav-item "> |
| <a class="nav-link" href="/search">Search</a> |
| </li> |
| </ul> |
| </div> |
| </nav> |
| |
| <main> |
| <div class="sidenav-top"> |
| <a href="/"><img src="/assets/img/gem5ColorLong.gif" height="80"></a> |
| <div class="search"> |
| <form action="/search" method="get"> |
| <!-- <label for="search-box"><i class="fa fa-search"></i></label> --> |
| <input type="text" name="query"> |
| <button type="submit" name="submit"><i class="fa fa-search"></i></button> |
| </form> |
| </div> |
| </div> |
| <div class="sidenav"> |
| <!-- Pull navigation from _data/documentation.yml --> |
| |
| <a class="item" href="/introduction" role="button" aria-expanded="false" aria-controls="collapseExample"> |
| Introduction |
| </a> |
| <div class="collapse " id="introduction"> |
| |
| </div> |
| |
| <a class="item" data-toggle="collapse" href="#pt1" role="button" aria-expanded="false" aria-controls="collapseExample"> |
| Getting Started |
| </a> |
| <div class="collapse " id="pt1"> |
| |
| <a class="subitem " href="/building">Building gem5</a> |
| |
| <a class="subitem " href="/simple_config">Creating a simple configuration script</a> |
| |
| <a class="subitem " href="/cache_config">Adding cache to configuration script</a> |
| |
| <a class="subitem " href="/gem5_stats">Understanding gem5 statistics and output</a> |
| |
| <a class="subitem " href="/example_configs">Using the default configuration scripts</a> |
| |
| </div> |
| |
| <a class="item" data-toggle="collapse" href="#pt2" role="button" aria-expanded="false" aria-controls="collapseExample"> |
| Modifying/Extending |
| </a> |
| <div class="collapse " id="pt2"> |
| |
| <a class="subitem " href="/environment">Setting up your development environment</a> |
| |
| <a class="subitem " href="/helloobject">Creating a very simple SimObject</a> |
| |
| <a class="subitem " href="/debugging">Debugging gem5</a> |
| |
| <a class="subitem " href="/events">Event-driven programming</a> |
| |
| <a class="subitem " href="/parameters">Adding parameters to SimObjects and more events</a> |
| |
| <a class="subitem " href="/memoryobject">Creating SimObjects in the memory system</a> |
| |
| <a class="subitem " href="/simplecache">Creating a simple cache object</a> |
| |
| </div> |
| |
| <a class="item" data-toggle="collapse" href="#pt3" role="button" aria-expanded="false" aria-controls="collapseExample"> |
| Modeling Cache Coherence with Ruby |
| </a> |
| <div class="collapse show" id="pt3"> |
| |
| <a class="subitem " href="/MSIintro">Introduction to Ruby</a> |
| |
| <a class="subitem " href="/cache-intro">MSI example cache protocol</a> |
| |
| <a class="subitem " href="/cache-declarations">Declaring a state machine</a> |
| |
| <a class="subitem " href="/cache-in-ports">In port code blocks</a> |
| |
| <a class="subitem " href="/cache-actions">Action code blocks</a> |
| |
| <a class="subitem " href="/cache-transitions">Transition code blocks</a> |
| |
| <a class="subitem active" href="/directory">MSI Directory implementation</a> |
| |
| <a class="subitem " href="/MSIbuilding">Compiling a SLICC protocol</a> |
| |
| <a class="subitem " href="/configuration">Configuring a simple Ruby system</a> |
| |
| <a class="subitem " href="/running">Running the simple Ruby system</a> |
| |
| <a class="subitem " href="/MSIdebugging">Debugging SLICC Protocols</a> |
| |
| <a class="subitem " href="/simple-MI_example">Configuring for a standard protocol</a> |
| |
| </div> |
| |
| </div> |
| |
| <div class="container" id="doc-container"> |
| <div class="edit"><a href="https://github.com/gem5/new-website/tree/master/_pages/documentation/part3/directory.md">Edit this page</a></div> |
| <dl> |
| <dt>authors</dt> |
| <dd>Jason Lowe-Power</dd> |
| </dl> |
| |
| <h1 id="msi-directory-implementation">MSI Directory implementation</h1> |
| |
| <p>Implementing a directory controller is very similar to the L1 cache |
| controller, except using a different state machine table. The state |
| machine fore the directory can be found in Table 8.2 in Sorin et al. |
| Since things are mostly similar to the L1 cache, this section mostly |
| just discusses a few more SLICC details and a few differences between |
| directory controllers and cache controllers. Let’s dive straight in and |
| start modifying a new file <code class="highlighter-rouge">MSI-dir.sm</code>.</p> |
| |
| <p>``` {.sourceCode .c++} |
| machine(MachineType:Directory, “Directory protocol”) |
| : |
| DirectoryMemory * directory; |
| Cycles toMemLatency := 1;</p> |
| |
| <p>MessageBuffer *forwardToCache, network=”To”, virtual_network=”1”, |
| vnet_type=”forward”; |
| MessageBuffer *responseToCache, network=”To”, virtual_network=”2”, |
| vnet_type=”response”;</p> |
| |
| <p>MessageBuffer *requestFromCache, network=”From”, virtual_network=”0”, |
| vnet_type=”request”;</p> |
| |
| <p>MessageBuffer *responseFromCache, network=”From”, virtual_network=”2”, |
| vnet_type=”response”;</p> |
| |
| <p>MessageBuffer *responseFromMemory;</p> |
| |
| <p>{ |
| . . . |
| }</p> |
| <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> |
| First, there are two parameter to this directory controller, |
| `DirectoryMemory` and a `toMemLatency`. The `DirectoryMemory` is a |
| little weird. It is allocated at initialization time such that it can |
| cover *all* of physical memory, like a complete directory *not a |
| directory cache*. I.e., there are pointers in the `DirectoryMemory` |
| object for every 64-byte block in physical memory. However, the actual |
| entries (as defined below) are lazily created via `getDirEntry()`. We'll |
| see more details about `DirectoryMemory` below. |
| |
| Next, is the `toMemLatency` parameter. This will be used in the |
| `enqueue` function when enqueuing requests to model the directory |
| latency. We didn't use a parameter for this in the L1 cache, but it is |
| simple to make the controller latency parameterized. This parameter |
| defaults to 1 cycle. It is not required to set a default here. The |
| default is propagated to the generated SimObject description file as the |
| default to the SimObject parameter. |
| |
| Next, we have the message buffers for the directory. Importantly, *these |
| need to have the same virtual network numbers* as the message buffers in |
| the L1 cache. These virtual network numbers are how the Ruby network |
| directs messages between controllers. |
| |
| There is also one more special message buffer: `responseFromMemory`. |
| This is similar to the `mandatoryQueue`, except instead of being like a |
| slave port for CPUs it is like a master port. The `responseFromMemory` |
| buffer will deliver response sent across the the memory port, as we will |
| see below in the action section. |
| |
| After the parameters and message buffers, we need to declare all of the |
| states, events, and other local structures. |
| |
| ``` {.sourceCode .c++} |
| state_declaration(State, desc="Directory states", |
| default="Directory_State_I") { |
| // Stable states. |
| // NOTE: These are "cache-centric" states like in Sorin et al. |
| // However, The access permissions are memory-centric. |
| I, AccessPermission:Read_Write, desc="Invalid in the caches."; |
| S, AccessPermission:Read_Only, desc="At least one cache has the blk"; |
| M, AccessPermission:Invalid, desc="A cache has the block in M"; |
| |
| // Transient states |
| S_D, AccessPermission:Busy, desc="Moving to S, but need data"; |
| |
| // Waiting for data from memory |
| S_m, AccessPermission:Read_Write, desc="In S waiting for mem"; |
| M_m, AccessPermission:Read_Write, desc="Moving to M waiting for mem"; |
| |
| // Waiting for write-ack from memory |
| MI_m, AccessPermission:Busy, desc="Moving to I waiting for ack"; |
| SS_m, AccessPermission:Busy, desc="Moving to I waiting for ack"; |
| } |
| |
| enumeration(Event, desc="Directory events") { |
| // Data requests from the cache |
| GetS, desc="Request for read-only data from cache"; |
| GetM, desc="Request for read-write data from cache"; |
| |
| // Writeback requests from the cache |
| PutSNotLast, desc="PutS and the block has other sharers"; |
| PutSLast, desc="PutS and the block has no other sharers"; |
| PutMOwner, desc="Dirty data writeback from the owner"; |
| PutMNonOwner, desc="Dirty data writeback from non-owner"; |
| |
| // Cache responses |
| Data, desc="Response to fwd request with data"; |
| |
| // From Memory |
| MemData, desc="Data from memory"; |
| MemAck, desc="Ack from memory that write is complete"; |
| } |
| |
| structure(Entry, desc="...", interface="AbstractEntry") { |
| State DirState, desc="Directory state"; |
| NetDest Sharers, desc="Sharers for this block"; |
| NetDest Owner, desc="Owner of this block"; |
| } |
| </code></pre></div></div> |
| |
| <p>In the <code class="highlighter-rouge">state_declaration</code> we define a default. For many things in SLICC |
| you can specify a default. However, this default must use the C++ name |
| (mangled SLICC name). For the state below you have to use the controller |
| name and the name we use for states. In this case, since the name of the |
| machine is “Directory” the name for “I” is “Directory”+”State” (for the |
| name of the structure)+”I”.</p> |
| |
| <p>Note that the permissions in the directory are “memory-centric”. |
| Whereas, all of the states are cache centric as in Sorin et al.</p> |
| |
| <p>In the <code class="highlighter-rouge">Entry</code> definition for the directory, we use a NetDest for both |
| the sharers and the owner. This makes sense for the sharers, since we |
| want a full bitvector for all L1 caches that may be sharing the block. |
| The reason we also use a <code class="highlighter-rouge">NetDest</code> for the owner is to simply copy the |
| structure into the message we send as a response as shown below.</p> |
| |
| <p>In this implementation, we use a few more transient states than in Table |
| 8.2 in Sorin et al. to deal with the fact that the memory latency in |
| unknown. In Sorin et al., the authors assume that the directory state |
| and memory data is stored together in main-memory to simplify the |
| protocol. Similarly, we also include new actions: the responses from |
| memory.</p> |
| |
| <p>Next, we have the functions that need to overridden and declared. The |
| function <code class="highlighter-rouge">getDirectoryEntry</code> either returns the valid directory entry, |
| or, if it hasn’t been allocated yet, this allocates the entry. |
| Implementing it this way may save some host memory since this is lazily |
| populated.</p> |
| |
| <p>``` {.sourceCode .c++} |
| Tick clockEdge();</p> |
| |
| <p>Entry getDirectoryEntry(Addr addr), return_by_pointer = “yes” { |
| Entry dir_entry := static_cast(Entry, “pointer”, directory[addr]); |
| if (is_invalid(dir_entry)) { |
| // This first time we see this address allocate an entry for it. |
| dir_entry := static_cast(Entry, “pointer”, |
| directory.allocate(addr, new Entry)); |
| } |
| return dir_entry; |
| }</p> |
| |
| <p>State getState(Addr addr) { |
| if (directory.isPresent(addr)) { |
| return getDirectoryEntry(addr).DirState; |
| } else { |
| return State:I; |
| } |
| }</p> |
| |
| <p>void setState(Addr addr, State state) { |
| if (directory.isPresent(addr)) { |
| if (state == State:M) { |
| DPRINTF(RubySlicc, “Owner %s\n”, getDirectoryEntry(addr).Owner); |
| assert(getDirectoryEntry(addr).Owner.count() == 1); |
| assert(getDirectoryEntry(addr).Sharers.count() == 0); |
| } |
| getDirectoryEntry(addr).DirState := state; |
| if (state == State:I) { |
| assert(getDirectoryEntry(addr).Owner.count() == 0); |
| assert(getDirectoryEntry(addr).Sharers.count() == 0); |
| } |
| } |
| }</p> |
| |
| <p>AccessPermission getAccessPermission(Addr addr) { |
| if (directory.isPresent(addr)) { |
| Entry e := getDirectoryEntry(addr); |
| return Directory_State_to_permission(e.DirState); |
| } else { |
| return AccessPermission:NotPresent; |
| } |
| } |
| void setAccessPermission(Addr addr, State state) { |
| if (directory.isPresent(addr)) { |
| Entry e := getDirectoryEntry(addr); |
| e.changePermission(Directory_State_to_permission(state)); |
| } |
| }</p> |
| |
| <p>void functionalRead(Addr addr, Packet *pkt) { |
| functionalMemoryRead(pkt); |
| }</p> |
| |
| <p>int functionalWrite(Addr addr, Packet *pkt) { |
| if (functionalMemoryWrite(pkt)) { |
| return 1; |
| } else { |
| return 0; |
| }</p> |
| <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> |
| Next, we need to implement the ports for the cache. First we specify the |
| `out_port` and then the `in_port` code blocks. The only difference |
| between the `in_port` in the directory and in the L1 cache is that the |
| directory does not have a TBE or cache entry. Thus, we do not pass |
| either into the `trigger` function. |
| |
| ``` {.sourceCode .c++} |
| out_port(forward_out, RequestMsg, forwardToCache); |
| out_port(response_out, ResponseMsg, responseToCache); |
| |
| in_port(memQueue_in, MemoryMsg, responseFromMemory) { |
| if (memQueue_in.isReady(clockEdge())) { |
| peek(memQueue_in, MemoryMsg) { |
| if (in_msg.Type == MemoryRequestType:MEMORY_READ) { |
| trigger(Event:MemData, in_msg.addr); |
| } else if (in_msg.Type == MemoryRequestType:MEMORY_WB) { |
| trigger(Event:MemAck, in_msg.addr); |
| } else { |
| error("Invalid message"); |
| } |
| } |
| } |
| } |
| |
| in_port(response_in, ResponseMsg, responseFromCache) { |
| if (response_in.isReady(clockEdge())) { |
| peek(response_in, ResponseMsg) { |
| if (in_msg.Type == CoherenceResponseType:Data) { |
| trigger(Event:Data, in_msg.addr); |
| } else { |
| error("Unexpected message type."); |
| } |
| } |
| } |
| } |
| |
| in_port(request_in, RequestMsg, requestFromCache) { |
| if (request_in.isReady(clockEdge())) { |
| peek(request_in, RequestMsg) { |
| Entry e := getDirectoryEntry(in_msg.addr); |
| if (in_msg.Type == CoherenceRequestType:GetS) { |
| |
| trigger(Event:GetS, in_msg.addr); |
| } else if (in_msg.Type == CoherenceRequestType:GetM) { |
| trigger(Event:GetM, in_msg.addr); |
| } else if (in_msg.Type == CoherenceRequestType:PutS) { |
| assert(is_valid(e)); |
| // If there is only a single sharer (i.e., the requestor) |
| if (e.Sharers.count() == 1) { |
| assert(e.Sharers.isElement(in_msg.Requestor)); |
| trigger(Event:PutSLast, in_msg.addr); |
| } else { |
| trigger(Event:PutSNotLast, in_msg.addr); |
| } |
| } else if (in_msg.Type == CoherenceRequestType:PutM) { |
| assert(is_valid(e)); |
| if (e.Owner.isElement(in_msg.Requestor)) { |
| trigger(Event:PutMOwner, in_msg.addr); |
| } else { |
| trigger(Event:PutMNonOwner, in_msg.addr); |
| } |
| } else { |
| error("Unexpected message type."); |
| } |
| } |
| } |
| } |
| </code></pre></div></div> |
| |
| <p>The next part of the state machine file is the actions. First, we define |
| actions for queuing memory reads and writes. For this, we will use a |
| special function define in the <code class="highlighter-rouge">AbstractController</code>: <code class="highlighter-rouge">queueMemoryRead</code>. |
| This function takes an address and converts it to a gem5 request and |
| packet and sends it to across the port that is connected to this |
| controller. We will see how to connect this port in the |
| configuration section <MSI-config-section>. Note that we need two |
| different actions to send data to memory for both requests and responses |
| since there are two different message buffers (virtual networks) that |
| data might arrive on.</p> |
| |
| <p>``` {.sourceCode .c++} |
| action(sendMemRead, “r”, desc=”Send a memory read request”) { |
| peek(request_in, RequestMsg) { |
| queueMemoryRead(in_msg.Requestor, address, toMemLatency); |
| } |
| }</p> |
| |
| <p>action(sendDataToMem, “w”, desc=”Write data to memory”) { |
| peek(request_in, RequestMsg) { |
| DPRINTF(RubySlicc, “Writing memory for %#x\n”, address); |
| DPRINTF(RubySlicc, “Writing %s\n”, in_msg.DataBlk); |
| queueMemoryWrite(in_msg.Requestor, address, toMemLatency, |
| in_msg.DataBlk); |
| } |
| }</p> |
| |
| <p>action(sendRespDataToMem, “rw”, desc=”Write data to memory from resp”) { |
| peek(response_in, ResponseMsg) { |
| DPRINTF(RubySlicc, “Writing memory for %#x\n”, address); |
| DPRINTF(RubySlicc, “Writing %s\n”, in_msg.DataBlk); |
| queueMemoryWrite(in_msg.Sender, address, toMemLatency, |
| in_msg.DataBlk); |
| } |
| }</p> |
| <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> |
| In this code, we also see the last way to add debug information to SLICC |
| protocols: `DPRINTF`. This is exactly the same as a `DPRINTF` in gem5, |
| except in SLICC only the `RubySlicc` debug flag is available. |
| |
| Next, we specify actions to update the sharers and owner of a particular |
| block. |
| |
| ``` {.sourceCode .c++} |
| action(addReqToSharers, "aS", desc="Add requestor to sharer list") { |
| peek(request_in, RequestMsg) { |
| getDirectoryEntry(address).Sharers.add(in_msg.Requestor); |
| } |
| } |
| |
| action(setOwner, "sO", desc="Set the owner") { |
| peek(request_in, RequestMsg) { |
| getDirectoryEntry(address).Owner.add(in_msg.Requestor); |
| } |
| } |
| |
| action(addOwnerToSharers, "oS", desc="Add the owner to sharers") { |
| Entry e := getDirectoryEntry(address); |
| assert(e.Owner.count() == 1); |
| e.Sharers.addNetDest(e.Owner); |
| } |
| |
| action(removeReqFromSharers, "rS", desc="Remove requestor from sharers") { |
| peek(request_in, RequestMsg) { |
| getDirectoryEntry(address).Sharers.remove(in_msg.Requestor); |
| } |
| } |
| |
| action(clearSharers, "cS", desc="Clear the sharer list") { |
| getDirectoryEntry(address).Sharers.clear(); |
| } |
| |
| action(clearOwner, "cO", desc="Clear the owner") { |
| getDirectoryEntry(address).Owner.clear(); |
| } |
| </code></pre></div></div> |
| |
| <p>The next set of actions send invalidates and forward requests to caches |
| that the directory cannot deal with alone.</p> |
| |
| <p>``` {.sourceCode .c++} |
| action(sendInvToSharers, “i”, desc=”Send invalidate to all sharers”) { |
| peek(request_in, RequestMsg) { |
| enqueue(forward_out, RequestMsg, 1) { |
| out_msg.addr := address; |
| out_msg.Type := CoherenceRequestType:Inv; |
| out_msg.Requestor := in_msg.Requestor; |
| out_msg.Destination := getDirectoryEntry(address).Sharers; |
| out_msg.MessageSize := MessageSizeType:Control; |
| } |
| } |
| }</p> |
| |
| <p>action(sendFwdGetS, “fS”, desc=”Send forward getS to owner”) { |
| assert(getDirectoryEntry(address).Owner.count() == 1); |
| peek(request_in, RequestMsg) { |
| enqueue(forward_out, RequestMsg, 1) { |
| out_msg.addr := address; |
| out_msg.Type := CoherenceRequestType:GetS; |
| out_msg.Requestor := in_msg.Requestor; |
| out_msg.Destination := getDirectoryEntry(address).Owner; |
| out_msg.MessageSize := MessageSizeType:Control; |
| } |
| } |
| }</p> |
| |
| <p>action(sendFwdGetM, “fM”, desc=”Send forward getM to owner”) { |
| assert(getDirectoryEntry(address).Owner.count() == 1); |
| peek(request_in, RequestMsg) { |
| enqueue(forward_out, RequestMsg, 1) { |
| out_msg.addr := address; |
| out_msg.Type := CoherenceRequestType:GetM; |
| out_msg.Requestor := in_msg.Requestor; |
| out_msg.Destination := getDirectoryEntry(address).Owner; |
| out_msg.MessageSize := MessageSizeType:Control; |
| } |
| } |
| }</p> |
| <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> |
| Now we have responses from the directory. Here we are peeking into the |
| special buffer `responseFromMemory`. You can find the definition of |
| `MemoryMsg` in `src/mem/protocol/RubySlicc_MemControl.sm`. |
| |
| ``` {.sourceCode .c++} |
| action(sendDataToReq, "d", desc="Send data from memory to requestor. May need to send sharer number, too") { |
| peek(memQueue_in, MemoryMsg) { |
| enqueue(response_out, ResponseMsg, 1) { |
| out_msg.addr := address; |
| out_msg.Type := CoherenceResponseType:Data; |
| out_msg.Sender := machineID; |
| out_msg.Destination.add(in_msg.OriginalRequestorMachId); |
| out_msg.DataBlk := in_msg.DataBlk; |
| out_msg.MessageSize := MessageSizeType:Data; |
| Entry e := getDirectoryEntry(address); |
| // Only need to include acks if we are the owner. |
| if (e.Owner.isElement(in_msg.OriginalRequestorMachId)) { |
| out_msg.Acks := e.Sharers.count(); |
| } else { |
| out_msg.Acks := 0; |
| } |
| assert(out_msg.Acks >= 0); |
| } |
| } |
| } |
| |
| action(sendPutAck, "a", desc="Send the put ack") { |
| peek(request_in, RequestMsg) { |
| enqueue(forward_out, RequestMsg, 1) { |
| out_msg.addr := address; |
| out_msg.Type := CoherenceRequestType:PutAck; |
| out_msg.Requestor := machineID; |
| out_msg.Destination.add(in_msg.Requestor); |
| out_msg.MessageSize := MessageSizeType:Control; |
| } |
| } |
| } |
| </code></pre></div></div> |
| |
| <p>Then, we have the queue management and stall actions.</p> |
| |
| <p>``` {.sourceCode .c++} |
| action(popResponseQueue, “pR”, desc=”Pop the response queue”) { |
| response_in.dequeue(clockEdge()); |
| }</p> |
| |
| <p>action(popRequestQueue, “pQ”, desc=”Pop the request queue”) { |
| request_in.dequeue(clockEdge()); |
| }</p> |
| |
| <p>action(popMemQueue, “pM”, desc=”Pop the memory queue”) { |
| memQueue_in.dequeue(clockEdge()); |
| }</p> |
| |
| <p>action(stall, “z”, desc=”Stall the incoming request”) { |
| // Do nothing. |
| }</p> |
| <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> |
| Finally, we have the transition section of the state machine file. These |
| mostly come from Table 8.2 in Sorin et al., but there are some extra |
| transitions to deal with the unknown memory latency. |
| |
| ``` {.sourceCode .c++} |
| transition({I, S}, GetS, S_m) { |
| sendMemRead; |
| addReqToSharers; |
| popRequestQueue; |
| } |
| |
| transition(I, {PutSNotLast, PutSLast, PutMNonOwner}) { |
| sendPutAck; |
| popRequestQueue; |
| } |
| |
| transition(S_m, MemData, S) { |
| sendDataToReq; |
| popMemQueue; |
| } |
| |
| transition(I, GetM, M_m) { |
| sendMemRead; |
| setOwner; |
| popRequestQueue; |
| } |
| |
| transition(M_m, MemData, M) { |
| sendDataToReq; |
| clearSharers; // NOTE: This isn't *required* in some cases. |
| popMemQueue; |
| } |
| |
| transition(S, GetM, M_m) { |
| sendMemRead; |
| removeReqFromSharers; |
| sendInvToSharers; |
| setOwner; |
| popRequestQueue; |
| } |
| |
| transition({S, S_D, SS_m, S_m}, {PutSNotLast, PutMNonOwner}) { |
| removeReqFromSharers; |
| sendPutAck; |
| popRequestQueue; |
| } |
| |
| transition(S, PutSLast, I) { |
| removeReqFromSharers; |
| sendPutAck; |
| popRequestQueue; |
| } |
| |
| transition(M, GetS, S_D) { |
| sendFwdGetS; |
| addReqToSharers; |
| addOwnerToSharers; |
| clearOwner; |
| popRequestQueue; |
| } |
| |
| transition(M, GetM) { |
| sendFwdGetM; |
| clearOwner; |
| setOwner; |
| popRequestQueue; |
| } |
| |
| transition({M, M_m, MI_m}, {PutSNotLast, PutSLast, PutMNonOwner}) { |
| sendPutAck; |
| popRequestQueue; |
| } |
| |
| transition(M, PutMOwner, MI_m) { |
| sendDataToMem; |
| clearOwner; |
| sendPutAck; |
| popRequestQueue; |
| } |
| |
| transition(MI_m, MemAck, I) { |
| popMemQueue; |
| } |
| |
| transition(S_D, {GetS, GetM}) { |
| stall; |
| } |
| |
| transition(S_D, PutSLast) { |
| removeReqFromSharers; |
| sendPutAck; |
| popRequestQueue; |
| } |
| |
| transition(S_D, Data, SS_m) { |
| sendRespDataToMem; |
| popResponseQueue; |
| } |
| |
| transition(SS_m, MemAck, S) { |
| popMemQueue; |
| } |
| |
| // If we get another request for a block that's waiting on memory, |
| // stall that request. |
| transition({MI_m, SS_m, S_m, M_m}, {GetS, GetM}) { |
| stall; |
| } |
| </code></pre></div></div> |
| |
| <p>You can download the complete <code class="highlighter-rouge">MSI-dir.sm</code> file |
| here <../../_static/scripts/part3/MSI_protocol/MSI-dir.sm>.</p> |
| |
| <br> |
| |
| <!-- RETRIVE PREVIOUS PAGE LINK --> |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| <!-- RETRIEVE NEXT PAGE LINK --> |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| <div class="navbuttons"> |
| |
| <a href="/cache-transitions"><button type="button" class="btn btn-outline-primary">PREVIOUS</button></a> |
| |
| |
| |
| <a href="/MSIbuilding"><button type="button" class="btn btn-outline-primary">NEXT</button></a> |
| |
| </div> |
| </div> |
| |
| </main> |
| |
| |
| <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script> |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script> |
| <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script> |
| |
| <script> |
| // When the user scrolls down 20px from the top of the document, show the button |
| window.onscroll = function() {scrollFunction()}; |
| |
| function scrollFunction() { |
| if (document.body.scrollTop > 100 || document.documentElement.scrollTop > 20) { |
| document.getElementById("myBtn").style.display = "block"; |
| } else { |
| document.getElementById("myBtn").style.display = "none"; |
| } |
| } |
| |
| // When the user clicks on the button, scroll to the top of the document |
| function topFunction() { |
| document.body.scrollTop = 0; |
| document.documentElement.scrollTop = 0; |
| } |
| </script> |
| |
| </body> |
| |
| |
| </html> |