MSI-dir.sm revision 12608:27b20e3ea77b
1/*
2 * Copyright (c) 2017 Jason Lowe-Power
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met: redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer;
9 * redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution;
12 * neither the name of the copyright holders nor the names of its
13 * contributors may be used to endorse or promote products derived from
14 * this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/**
30 * This file contains the directory controller of a simple example MSI protocol
31 *
32 * In Ruby the directory controller both contains the directory coherence state
33 * but also functions as the memory controller in many ways. There are states
34 * in the directory that are both memory-centric and cache-centric. Be careful!
35 *
36 * The protocol in this file is based off of the MSI protocol found in
37 * A Primer on Memory Consistency and Cache Coherence
38 *      Daniel J. Sorin, Mark D. Hill, and David A. Wood
39 *      Synthesis Lectures on Computer Architecture 2011 6:3, 141-149
40 *
41 * Table 8.2 contains the transitions and actions found in this file and
42 * section 8.2.4 explains the protocol in detail.
43 *
44 * See Learning gem5 Part 3: Ruby for more details.
45 *
46 * Authors: Jason Lowe-Power
47 */
48
49machine(MachineType:Directory, "Directory protocol")
50    :
51      // This "DirectoryMemory" is a little weird. It is initially allocated
52      // so that it *can* cover all of memory (i.e., there are pointers for
53      // every 64-byte block in memory). However, the entries are lazily
54      // created in getDirEntry()
55      DirectoryMemory * directory;
56      // You can put any parameters you want here. They will be exported as
57      // normal SimObject parameters (like in the SimObject description file)
58      // and you can set these parameters at runtime via the python config
59      // file. If there is no default here (like directory), it is mandatory
60      // to set the parameter in the python config. Otherwise, it uses the
61      // default value set here.
62      Cycles toMemLatency := 1;
63
64    // Forwarding requests from the directory *to* the caches.
65    MessageBuffer *forwardToCache, network="To", virtual_network="1",
66          vnet_type="forward";
67    // Response from the directory *to* the cache.
68    MessageBuffer *responseToCache, network="To", virtual_network="2",
69          vnet_type="response";
70
71    // Requests *from* the cache to the directory
72    MessageBuffer *requestFromCache, network="From", virtual_network="0",
73          vnet_type="request";
74
75    // Responses *from* the cache to the directory
76    MessageBuffer *responseFromCache, network="From", virtual_network="2",
77          vnet_type="response";
78
79    // Special buffer for memory responses. Kind of like the mandatory queue
80    MessageBuffer *responseFromMemory;
81
82{
83    // For many things in SLICC you can specify a default. However, this
84    // default must use the C++ name (mangled SLICC name). For the state below
85    // you have to use the controller name and the name we use for states.
86    state_declaration(State, desc="Directory states",
87                      default="Directory_State_I") {
88        // Stable states.
89        // NOTE: Thise are "cache-centric" states like in Sorin et al.
90        // However, The access permissions are memory-centric.
91        I, AccessPermission:Read_Write,  desc="Invalid in the caches.";
92        S, AccessPermission:Read_Only,   desc="At least one cache has the blk";
93        M, AccessPermission:Invalid,     desc="A cache has the block in M";
94
95        // Transient states
96        S_D, AccessPermission:Busy,      desc="Moving to S, but need data";
97
98        // Waiting for data from memory
99        S_m, AccessPermission:Read_Write, desc="In S waiting for mem";
100        M_m, AccessPermission:Read_Write, desc="Moving to M waiting for mem";
101
102        // Waiting for write-ack from memory
103        MI_m, AccessPermission:Busy,       desc="Moving to I waiting for ack";
104        SS_m, AccessPermission:Busy,       desc="Moving to S waiting for ack";
105    }
106
107    enumeration(Event, desc="Directory events") {
108        // Data requests from the cache
109        GetS,         desc="Request for read-only data from cache";
110        GetM,         desc="Request for read-write data from cache";
111
112        // Writeback requests from the cache
113        PutSNotLast,  desc="PutS and the block has other sharers";
114        PutSLast,     desc="PutS and the block has no other sharers";
115        PutMOwner,    desc="Dirty data writeback from the owner";
116        PutMNonOwner, desc="Dirty data writeback from non-owner";
117
118        // Cache responses
119        Data,         desc="Response to fwd request with data";
120
121        // From Memory
122        MemData,      desc="Data from memory";
123        MemAck,       desc="Ack from memory that write is complete";
124    }
125
126    // NOTE: We use a netdest for the sharers and the owner so we can simply
127    // copy the structure into the message we send as a response.
128    structure(Entry, desc="...", interface="AbstractEntry") {
129        State DirState,         desc="Directory state";
130        NetDest Sharers,        desc="Sharers for this block";
131        NetDest Owner,          desc="Owner of this block";
132    }
133
134    Tick clockEdge();
135
136    // This either returns the valid directory entry, or, if it hasn't been
137    // allocated yet, this allocates the entry. This may save some host memory
138    // since this is lazily populated.
139    Entry getDirectoryEntry(Addr addr), return_by_pointer = "yes" {
140        Entry dir_entry := static_cast(Entry, "pointer", directory[addr]);
141        if (is_invalid(dir_entry)) {
142            // This first time we see this address allocate an entry for it.
143            dir_entry := static_cast(Entry, "pointer",
144                                     directory.allocate(addr, new Entry));
145        }
146        return dir_entry;
147    }
148
149    /*************************************************************************/
150    // Functions that we need to define/override to use our specific structures
151    // in this implementation.
152    // NOTE: we don't have TBE in this machine, so we don't need to pass it
153    // to these overridden functions.
154
155    State getState(Addr addr) {
156        if (directory.isPresent(addr)) {
157            return getDirectoryEntry(addr).DirState;
158        } else {
159            return State:I;
160        }
161    }
162
163    void setState(Addr addr, State state) {
164        if (directory.isPresent(addr)) {
165            if (state == State:M) {
166                DPRINTF(RubySlicc, "Owner %s\n", getDirectoryEntry(addr).Owner);
167                assert(getDirectoryEntry(addr).Owner.count() == 1);
168                assert(getDirectoryEntry(addr).Sharers.count() == 0);
169            }
170            getDirectoryEntry(addr).DirState := state;
171            if (state == State:I)  {
172                assert(getDirectoryEntry(addr).Owner.count() == 0);
173                assert(getDirectoryEntry(addr).Sharers.count() == 0);
174            }
175        }
176    }
177
178    // This is really the access permissions of memory.
179    // TODO: I don't understand this at the directory.
180    AccessPermission getAccessPermission(Addr addr) {
181        if (directory.isPresent(addr)) {
182            Entry e := getDirectoryEntry(addr);
183            return Directory_State_to_permission(e.DirState);
184        } else  {
185            return AccessPermission:NotPresent;
186        }
187    }
188    void setAccessPermission(Addr addr, State state) {
189        if (directory.isPresent(addr)) {
190            Entry e := getDirectoryEntry(addr);
191            e.changePermission(Directory_State_to_permission(state));
192        }
193    }
194
195    void functionalRead(Addr addr, Packet *pkt) {
196        functionalMemoryRead(pkt);
197    }
198
199    // This returns the number of writes. So, if we write then return 1
200    int functionalWrite(Addr addr, Packet *pkt) {
201        if (functionalMemoryWrite(pkt)) {
202            return 1;
203        } else {
204            return 0;
205        }
206    }
207
208
209    /*************************************************************************/
210    // Network ports
211
212    out_port(forward_out, RequestMsg, forwardToCache);
213    out_port(response_out, ResponseMsg, responseToCache);
214
215    in_port(memQueue_in, MemoryMsg, responseFromMemory) {
216        if (memQueue_in.isReady(clockEdge())) {
217            peek(memQueue_in, MemoryMsg) {
218                if (in_msg.Type == MemoryRequestType:MEMORY_READ) {
219                    trigger(Event:MemData, in_msg.addr);
220                } else if (in_msg.Type == MemoryRequestType:MEMORY_WB) {
221                    trigger(Event:MemAck, in_msg.addr);
222                } else {
223                    error("Invalid message");
224                }
225            }
226        }
227    }
228
229    in_port(response_in, ResponseMsg, responseFromCache) {
230        if (response_in.isReady(clockEdge())) {
231            peek(response_in, ResponseMsg) {
232                if (in_msg.Type == CoherenceResponseType:Data) {
233                    trigger(Event:Data, in_msg.addr);
234                } else {
235                    error("Unexpected message type.");
236                }
237            }
238        }
239    }
240
241    in_port(request_in, RequestMsg, requestFromCache) {
242        if (request_in.isReady(clockEdge())) {
243            peek(request_in, RequestMsg) {
244                Entry entry := getDirectoryEntry(in_msg.addr);
245                if (in_msg.Type == CoherenceRequestType:GetS) {
246                    // NOTE: Since we don't have a TBE in this machine, there
247                    // is no need to pass a TBE into trigger. Also, for the
248                    // directory there is no cache entry.
249                    trigger(Event:GetS, in_msg.addr);
250                } else if (in_msg.Type == CoherenceRequestType:GetM) {
251                    trigger(Event:GetM, in_msg.addr);
252                } else if (in_msg.Type == CoherenceRequestType:PutS) {
253                    assert(is_valid(entry));
254                    // If there is only a single sharer (i.e., the requestor)
255                    if (entry.Sharers.count() == 1) {
256                        assert(entry.Sharers.isElement(in_msg.Requestor));
257                        trigger(Event:PutSLast, in_msg.addr);
258                    } else {
259                        trigger(Event:PutSNotLast, in_msg.addr);
260                    }
261                } else if (in_msg.Type == CoherenceRequestType:PutM) {
262                    assert(is_valid(entry));
263                    if (entry.Owner.isElement(in_msg.Requestor)) {
264                        trigger(Event:PutMOwner, in_msg.addr);
265                    } else {
266                        trigger(Event:PutMNonOwner, in_msg.addr);
267                    }
268                } else {
269                    error("Unexpected message type.");
270                }
271            }
272        }
273    }
274
275
276
277    /*************************************************************************/
278    // Actions
279
280    // Memory actions.
281
282    action(sendMemRead, "r", desc="Send a memory read request") {
283        peek(request_in, RequestMsg) {
284            // Special function from AbstractController that will send a new
285            // packet out of the "Ruby" black box to the memory side. At some
286            // point the response will be on the memory queue.
287            // Like enqeue, this takes a latency for the request.
288            queueMemoryRead(in_msg.Requestor, address, toMemLatency);
289        }
290    }
291
292    action(sendDataToMem, "w", desc="Write data to memory") {
293        peek(request_in, RequestMsg) {
294            DPRINTF(RubySlicc, "Writing memory for %#x\n", address);
295            DPRINTF(RubySlicc, "Writing %s\n", in_msg.DataBlk);
296            queueMemoryWrite(in_msg.Requestor, address, toMemLatency,
297                             in_msg.DataBlk);
298        }
299    }
300
301    action(sendRespDataToMem, "rw", desc="Write data to memory from resp") {
302        peek(response_in, ResponseMsg) {
303            DPRINTF(RubySlicc, "Writing memory for %#x\n", address);
304            DPRINTF(RubySlicc, "Writing %s\n", in_msg.DataBlk);
305            queueMemoryWrite(in_msg.Sender, address, toMemLatency,
306                             in_msg.DataBlk);
307        }
308    }
309
310    // Sharer/owner actions
311
312    action(addReqToSharers, "aS", desc="Add requestor to sharer list") {
313        peek(request_in, RequestMsg) {
314            getDirectoryEntry(address).Sharers.add(in_msg.Requestor);
315        }
316    }
317
318    action(setOwner, "sO", desc="Set the owner") {
319        peek(request_in, RequestMsg) {
320            getDirectoryEntry(address).Owner.add(in_msg.Requestor);
321        }
322    }
323
324    action(addOwnerToSharers, "oS", desc="Add the owner to sharers") {
325        Entry e := getDirectoryEntry(address);
326        assert(e.Owner.count() == 1);
327        e.Sharers.addNetDest(e.Owner);
328    }
329
330    action(removeReqFromSharers, "rS", desc="Remove requestor from sharers") {
331        peek(request_in, RequestMsg) {
332            getDirectoryEntry(address).Sharers.remove(in_msg.Requestor);
333        }
334    }
335
336    action(clearSharers, "cS", desc="Clear the sharer list") {
337        getDirectoryEntry(address).Sharers.clear();
338    }
339
340    action(clearOwner, "cO", desc="Clear the owner") {
341        getDirectoryEntry(address).Owner.clear();
342    }
343
344    // Invalidates and forwards
345
346    action(sendInvToSharers, "i", desc="Send invalidate to all sharers") {
347        peek(request_in, RequestMsg) {
348            enqueue(forward_out, RequestMsg, 1) {
349                out_msg.addr := address;
350                out_msg.Type := CoherenceRequestType:Inv;
351                out_msg.Requestor := in_msg.Requestor;
352                out_msg.Destination := getDirectoryEntry(address).Sharers;
353                out_msg.MessageSize := MessageSizeType:Control;
354            }
355        }
356    }
357
358    action(sendFwdGetS, "fS", desc="Send forward getS to owner") {
359        assert(getDirectoryEntry(address).Owner.count() == 1);
360        peek(request_in, RequestMsg) {
361            enqueue(forward_out, RequestMsg, 1) {
362                out_msg.addr := address;
363                out_msg.Type := CoherenceRequestType:GetS;
364                out_msg.Requestor := in_msg.Requestor;
365                out_msg.Destination := getDirectoryEntry(address).Owner;
366                out_msg.MessageSize := MessageSizeType:Control;
367            }
368        }
369    }
370
371    action(sendFwdGetM, "fM", desc="Send forward getM to owner") {
372        assert(getDirectoryEntry(address).Owner.count() == 1);
373        peek(request_in, RequestMsg) {
374            enqueue(forward_out, RequestMsg, 1) {
375                out_msg.addr := address;
376                out_msg.Type := CoherenceRequestType:GetM;
377                out_msg.Requestor := in_msg.Requestor;
378                out_msg.Destination := getDirectoryEntry(address).Owner;
379                out_msg.MessageSize := MessageSizeType:Control;
380            }
381        }
382    }
383
384    // Responses to requests
385
386    // This also needs to send along the number of sharers!!!!
387    action(sendDataToReq, "d", desc="Send data from memory to requestor. ") {
388                                    //"May need to send sharer number, too") {
389        peek(memQueue_in, MemoryMsg) {
390            enqueue(response_out, ResponseMsg, 1) {
391                out_msg.addr := address;
392                out_msg.Type := CoherenceResponseType:Data;
393                out_msg.Sender := machineID;
394                out_msg.Destination.add(in_msg.OriginalRequestorMachId);
395                out_msg.DataBlk := in_msg.DataBlk;
396                out_msg.MessageSize := MessageSizeType:Data;
397                Entry e := getDirectoryEntry(address);
398                // Only need to include acks if we are the owner.
399                if (e.Owner.isElement(in_msg.OriginalRequestorMachId)) {
400                    out_msg.Acks := e.Sharers.count();
401                } else {
402                    out_msg.Acks := 0;
403                }
404                assert(out_msg.Acks >= 0);
405            }
406        }
407    }
408
409    action(sendPutAck, "a", desc="Send the put ack") {
410        peek(request_in, RequestMsg) {
411            enqueue(forward_out, RequestMsg, 1) {
412                out_msg.addr := address;
413                out_msg.Type := CoherenceRequestType:PutAck;
414                out_msg.Requestor := machineID;
415                out_msg.Destination.add(in_msg.Requestor);
416                out_msg.MessageSize := MessageSizeType:Control;
417            }
418        }
419    }
420
421    // Queue management
422
423    action(popResponseQueue, "pR", desc="Pop the response queue") {
424        response_in.dequeue(clockEdge());
425    }
426
427    action(popRequestQueue, "pQ", desc="Pop the request queue") {
428        request_in.dequeue(clockEdge());
429    }
430
431    action(popMemQueue, "pM", desc="Pop the memory queue") {
432        memQueue_in.dequeue(clockEdge());
433    }
434
435    // Stalling actions
436    action(stall, "z", desc="Stall the incoming request") {
437        // Do nothing.
438    }
439
440
441    /*************************************************************************/
442    // transitions
443
444    transition({I, S}, GetS, S_m) {
445        sendMemRead;
446        addReqToSharers;
447        popRequestQueue;
448    }
449
450    transition(I, {PutSNotLast, PutSLast, PutMNonOwner}) {
451        sendPutAck;
452        popRequestQueue;
453    }
454
455    transition(S_m, MemData, S) {
456        sendDataToReq;
457        popMemQueue;
458    }
459
460    transition(I, GetM, M_m) {
461        sendMemRead;
462        setOwner;
463        popRequestQueue;
464    }
465
466    transition(M_m, MemData, M) {
467        sendDataToReq;
468        clearSharers; // NOTE: This isn't *required* in some cases.
469        popMemQueue;
470    }
471
472    transition(S, GetM, M_m) {
473        sendMemRead;
474        removeReqFromSharers;
475        sendInvToSharers;
476        setOwner;
477        popRequestQueue;
478    }
479
480    transition({S, S_D, SS_m, S_m}, {PutSNotLast, PutMNonOwner}) {
481        removeReqFromSharers;
482        sendPutAck;
483        popRequestQueue;
484    }
485
486    transition(S, PutSLast, I) {
487        removeReqFromSharers;
488        sendPutAck;
489        popRequestQueue;
490    }
491
492    transition(M, GetS, S_D) {
493        sendFwdGetS;
494        addReqToSharers;
495        addOwnerToSharers;
496        clearOwner;
497        popRequestQueue;
498    }
499
500    transition(M, GetM) {
501        sendFwdGetM;
502        clearOwner;
503        setOwner;
504        popRequestQueue;
505    }
506
507    transition({M, M_m, MI_m}, {PutSNotLast, PutSLast, PutMNonOwner}) {
508        sendPutAck;
509        popRequestQueue;
510    }
511
512    transition(M, PutMOwner, MI_m) {
513        sendDataToMem;
514        clearOwner;
515        sendPutAck;
516        popRequestQueue;
517    }
518
519    transition(MI_m, MemAck, I) {
520        popMemQueue;
521    }
522
523    transition(S_D, {GetS, GetM}) {
524        stall;
525    }
526
527    transition(S_D, PutSLast) {
528        removeReqFromSharers;
529        sendPutAck;
530        popRequestQueue;
531    }
532
533    transition(S_D, Data, SS_m) {
534        sendRespDataToMem;
535        popResponseQueue;
536    }
537
538    transition(SS_m, MemAck, S) {
539        popMemQueue;
540    }
541
542    // If we get another request for a block that's waiting on memory,
543    // stall that request.
544    transition({MI_m, SS_m, S_m, M_m}, {GetS, GetM}) {
545        stall;
546    }
547
548}
549