112608Sjason@lowepower.com/*
212608Sjason@lowepower.com * Copyright (c) 2017 Jason Lowe-Power
312608Sjason@lowepower.com * All rights reserved.
412608Sjason@lowepower.com *
512608Sjason@lowepower.com * Redistribution and use in source and binary forms, with or without
612608Sjason@lowepower.com * modification, are permitted provided that the following conditions are
712608Sjason@lowepower.com * met: redistributions of source code must retain the above copyright
812608Sjason@lowepower.com * notice, this list of conditions and the following disclaimer;
912608Sjason@lowepower.com * redistributions in binary form must reproduce the above copyright
1012608Sjason@lowepower.com * notice, this list of conditions and the following disclaimer in the
1112608Sjason@lowepower.com * documentation and/or other materials provided with the distribution;
1212608Sjason@lowepower.com * neither the name of the copyright holders nor the names of its
1312608Sjason@lowepower.com * contributors may be used to endorse or promote products derived from
1412608Sjason@lowepower.com * this software without specific prior written permission.
1512608Sjason@lowepower.com *
1612608Sjason@lowepower.com * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1712608Sjason@lowepower.com * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1812608Sjason@lowepower.com * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1912608Sjason@lowepower.com * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2012608Sjason@lowepower.com * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2112608Sjason@lowepower.com * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2212608Sjason@lowepower.com * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2312608Sjason@lowepower.com * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2412608Sjason@lowepower.com * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2512608Sjason@lowepower.com * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2612608Sjason@lowepower.com * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2712608Sjason@lowepower.com */
2812608Sjason@lowepower.com
2912608Sjason@lowepower.com/**
3012608Sjason@lowepower.com * This file contains a simple example MSI protocol.
3112608Sjason@lowepower.com *
3212608Sjason@lowepower.com * The protocol in this file is based off of the MSI protocol found in
3312608Sjason@lowepower.com * A Primer on Memory Consistency and Cache Coherence
3412608Sjason@lowepower.com *      Daniel J. Sorin, Mark D. Hill, and David A. Wood
3512608Sjason@lowepower.com *      Synthesis Lectures on Computer Architecture 2011 6:3, 141-149
3612608Sjason@lowepower.com *
3712608Sjason@lowepower.com * Table 8.1 contains the transitions and actions found in this file and
3812608Sjason@lowepower.com * section 8.2.4 explains the protocol in detail.
3912608Sjason@lowepower.com *
4012608Sjason@lowepower.com * See Learning gem5 Part 3: Ruby for more details.
4112608Sjason@lowepower.com *
4212608Sjason@lowepower.com * Authors: Jason Lowe-Power
4312608Sjason@lowepower.com */
4412608Sjason@lowepower.com
4512608Sjason@lowepower.com/// Declare a machine with type L1Cache.
4612608Sjason@lowepower.commachine(MachineType:L1Cache, "MSI cache")
4712608Sjason@lowepower.com    : Sequencer *sequencer; // Incoming request from CPU come from this
4812608Sjason@lowepower.com      CacheMemory *cacheMemory; // This stores the data and cache states
4912608Sjason@lowepower.com      bool send_evictions; // Needed to support O3 CPU and mwait
5012608Sjason@lowepower.com
5112608Sjason@lowepower.com      // Other declarations
5212608Sjason@lowepower.com      // Message buffers are required to send and receive data from the Ruby
5312608Sjason@lowepower.com      // network. The from/to and request/response can be confusing!
5412608Sjason@lowepower.com      // Virtual networks are needed to prevent deadlock (e.g., it is bad if a
5512608Sjason@lowepower.com      // response gets stuck behind a stalled request). In this protocol, we are
5612608Sjason@lowepower.com      // using three virtual networks. The highest priority is responses,
5712608Sjason@lowepower.com      // followed by forwarded requests, then requests have the lowest priority.
5812608Sjason@lowepower.com
5912608Sjason@lowepower.com      // Requests *to* the directory
6012608Sjason@lowepower.com      MessageBuffer * requestToDir, network="To", virtual_network="0",
6112608Sjason@lowepower.com            vnet_type="request";
6212608Sjason@lowepower.com      // Responses *to* the directory or other caches
6312608Sjason@lowepower.com      MessageBuffer * responseToDirOrSibling, network="To", virtual_network="2",
6412608Sjason@lowepower.com            vnet_type="response";
6512608Sjason@lowepower.com
6612608Sjason@lowepower.com      // Requests *from* the directory for fwds, invs, and put acks.
6712608Sjason@lowepower.com      MessageBuffer * forwardFromDir, network="From", virtual_network="1",
6812608Sjason@lowepower.com            vnet_type="forward";
6912608Sjason@lowepower.com      // Responses *from* directory and other caches for this cache's reqs.
7012608Sjason@lowepower.com      MessageBuffer * responseFromDirOrSibling, network="From",
7112608Sjason@lowepower.com            virtual_network="2", vnet_type="response";
7212608Sjason@lowepower.com
7312608Sjason@lowepower.com      // This is all of the incoming requests from the core via the sequencer
7412608Sjason@lowepower.com      MessageBuffer * mandatoryQueue;
7512608Sjason@lowepower.com{
7612608Sjason@lowepower.com    // Declare the states that this cache will use. These are both stable
7712608Sjason@lowepower.com    // states (no underscore) and transient states (with underscore). Letters
7812608Sjason@lowepower.com    // after the underscores are superscript in Sorin et al.
7912608Sjason@lowepower.com    // Underscores and "desc" are used when generating HTML tables.
8012608Sjason@lowepower.com    // Access permissions are used for functional accesses. For reads, the
8112608Sjason@lowepower.com    // functional access reads *all* of the blocks with a matching address that
8212608Sjason@lowepower.com    // have read-only or read-write permission. For functional writes, all
8312608Sjason@lowepower.com    // blocks are updated with new data if they have busy, read-only, or
8412608Sjason@lowepower.com    // read-write permission.
8512608Sjason@lowepower.com    state_declaration(State, desc="Cache states") {
8612608Sjason@lowepower.com        I,      AccessPermission:Invalid,
8712608Sjason@lowepower.com                    desc="Not present/Invalid";
8812608Sjason@lowepower.com
8912608Sjason@lowepower.com        // States moving out of I
9012608Sjason@lowepower.com        IS_D,   AccessPermission:Invalid,
9112608Sjason@lowepower.com                    desc="Invalid, moving to S, waiting for data";
9212608Sjason@lowepower.com        IM_AD,  AccessPermission:Invalid,
9312608Sjason@lowepower.com                    desc="Invalid, moving to M, waiting for acks and data";
9412608Sjason@lowepower.com        IM_A,   AccessPermission:Busy,
9512608Sjason@lowepower.com                    desc="Invalid, moving to M, waiting for acks";
9612608Sjason@lowepower.com
9712608Sjason@lowepower.com        S,      AccessPermission:Read_Only,
9812608Sjason@lowepower.com                    desc="Shared. Read-only, other caches may have the block";
9912608Sjason@lowepower.com
10012608Sjason@lowepower.com        // States moving out of S
10112608Sjason@lowepower.com        SM_AD,  AccessPermission:Read_Only,
10212608Sjason@lowepower.com                    desc="Shared, moving to M, waiting for acks and 'data'";
10312608Sjason@lowepower.com        SM_A,   AccessPermission:Read_Only,
10412608Sjason@lowepower.com                    desc="Shared, moving to M, waiting for acks";
10512608Sjason@lowepower.com
10612608Sjason@lowepower.com        M,      AccessPermission:Read_Write,
10712608Sjason@lowepower.com                    desc="Modified. Read & write permissions. Owner of block";
10812608Sjason@lowepower.com
10912608Sjason@lowepower.com        // States moving to Invalid
11012608Sjason@lowepower.com        MI_A,   AccessPermission:Busy,
11112608Sjason@lowepower.com                    desc="Was modified, moving to I, waiting for put ack";
11212608Sjason@lowepower.com        SI_A,   AccessPermission:Busy,
11312608Sjason@lowepower.com                    desc="Was shared, moving to I, waiting for put ack";
11412608Sjason@lowepower.com        II_A,   AccessPermission:Invalid,
11512608Sjason@lowepower.com                    desc="Sent valid data before receiving put ack. ";
11612608Sjason@lowepower.com                         //"Waiting for put ack.";
11712608Sjason@lowepower.com    }
11812608Sjason@lowepower.com
11912608Sjason@lowepower.com    // Events that can be triggered on incoming messages. These are the events
12012608Sjason@lowepower.com    // that will trigger transitions
12112608Sjason@lowepower.com    enumeration(Event, desc="Cache events") {
12212608Sjason@lowepower.com        // From the processor/sequencer/mandatory queue
12312608Sjason@lowepower.com        Load,           desc="Load from processor";
12412608Sjason@lowepower.com        Store,          desc="Store from processor";
12512608Sjason@lowepower.com
12612608Sjason@lowepower.com        // Internal event (only triggered from processor requests)
12712608Sjason@lowepower.com        Replacement,    desc="Triggered when block is chosen as victim";
12812608Sjason@lowepower.com
12912608Sjason@lowepower.com        // Forwarded reqeust from other cache via dir on the forward network
13012608Sjason@lowepower.com        FwdGetS,        desc="Directory sent us a request to satisfy GetS. ";
13112608Sjason@lowepower.com                             //"We must have the block in M to respond to this.";
13212608Sjason@lowepower.com        FwdGetM,        desc="Directory sent us a request to satisfy GetM. ";
13312608Sjason@lowepower.com                             //"We must have the block in M to respond to this.";
13412608Sjason@lowepower.com        Inv,            desc="Invalidate from the directory.";
13512608Sjason@lowepower.com        PutAck,         desc="Response from directory after we issue a put. ";
13612608Sjason@lowepower.com                             //"This must be on the fwd network to avoid";
13712608Sjason@lowepower.com                             //"deadlock.";
13812608Sjason@lowepower.com
13912608Sjason@lowepower.com        // Responses from directory
14012608Sjason@lowepower.com        DataDirNoAcks,  desc="Data from directory (acks = 0)";
14112608Sjason@lowepower.com        DataDirAcks,    desc="Data from directory (acks > 0)";
14212608Sjason@lowepower.com
14312608Sjason@lowepower.com        // Responses from other caches
14412608Sjason@lowepower.com        DataOwner,      desc="Data from owner";
14512608Sjason@lowepower.com        InvAck,         desc="Invalidation ack from other cache after Inv";
14612608Sjason@lowepower.com
14712608Sjason@lowepower.com        // Special internally triggered event to simplify implementation
14812608Sjason@lowepower.com        LastInvAck,     desc="Triggered after the last ack is received";
14912608Sjason@lowepower.com    }
15012608Sjason@lowepower.com
15112608Sjason@lowepower.com    // A structure for the cache entry. This stores the cache data and state
15212608Sjason@lowepower.com    // as defined above. You can put any other information here you like.
15312608Sjason@lowepower.com    // The AbstractCacheEntry is defined in
15412608Sjason@lowepower.com    // src/mem/ruby/slic_interface/AbstractCacheEntry.hh
15512608Sjason@lowepower.com    // If you want to use any of the functions in the abstract entry declare
15612608Sjason@lowepower.com    // them here.
15712608Sjason@lowepower.com    structure(Entry, desc="Cache entry", interface="AbstractCacheEntry") {
15812608Sjason@lowepower.com        State CacheState,        desc="cache state";
15912608Sjason@lowepower.com        DataBlock DataBlk,       desc="Data in the block";
16012608Sjason@lowepower.com    }
16112608Sjason@lowepower.com
16212608Sjason@lowepower.com    // TBE is the "transaction buffer entry". This stores information needed
16312608Sjason@lowepower.com    // during transient states. This is *like* an MSHR. It functions as an MSHR
16412608Sjason@lowepower.com    // in this protocol, but the entry is also allocated for other uses.
16512608Sjason@lowepower.com    structure(TBE, desc="Entry for transient requests") {
16612608Sjason@lowepower.com        State TBEState,         desc="State of block";
16712608Sjason@lowepower.com        DataBlock DataBlk,      desc="Data for the block. Needed for MI_A";
16812608Sjason@lowepower.com        int AcksOutstanding, default=0, desc="Number of acks left to receive.";
16912608Sjason@lowepower.com    }
17012608Sjason@lowepower.com
17112608Sjason@lowepower.com    // Table of TBE entries. This is defined externally in
17212608Sjason@lowepower.com    // src/mem/ruby/structures/TBETable.hh. It is templatized on the TBE
17312608Sjason@lowepower.com    // structure defined above.
17412608Sjason@lowepower.com    structure(TBETable, external="yes") {
17512608Sjason@lowepower.com      TBE lookup(Addr);
17612608Sjason@lowepower.com      void allocate(Addr);
17712608Sjason@lowepower.com      void deallocate(Addr);
17812608Sjason@lowepower.com      bool isPresent(Addr);
17912608Sjason@lowepower.com    }
18012608Sjason@lowepower.com
18112608Sjason@lowepower.com    /*************************************************************************/
18212608Sjason@lowepower.com    // Some declarations of member functions and member variables.
18312608Sjason@lowepower.com
18412608Sjason@lowepower.com    // The TBE table for this machine. It is templatized under the covers.
18512608Sjason@lowepower.com    // NOTE: SLICC mangles names with the machine type. Thus, the TBE declared
18612608Sjason@lowepower.com    //       above will be L1Cache_TBE in C++.
18712608Sjason@lowepower.com    // We also have to pass through a parameter to the machine to the TBETable.
18812608Sjason@lowepower.com    TBETable TBEs, template="<L1Cache_TBE>", constructor="m_number_of_TBEs";
18912608Sjason@lowepower.com
19012608Sjason@lowepower.com    // Declare all of the functions of the AbstractController that we may use
19112608Sjason@lowepower.com    // in this file.
19212608Sjason@lowepower.com    // Functions from clocked object
19312608Sjason@lowepower.com    Tick clockEdge();
19412608Sjason@lowepower.com
19512608Sjason@lowepower.com    // Functions we must use to set things up for the transitions to execute
19612608Sjason@lowepower.com    // correctly.
19712608Sjason@lowepower.com    // These next set/unset functions are used to populate the implicit
19812608Sjason@lowepower.com    // variables used in actions. This is required when a transition has
19912608Sjason@lowepower.com    // multiple actions.
20012608Sjason@lowepower.com    void set_cache_entry(AbstractCacheEntry a);
20112608Sjason@lowepower.com    void unset_cache_entry();
20212608Sjason@lowepower.com    void set_tbe(TBE b);
20312608Sjason@lowepower.com    void unset_tbe();
20412608Sjason@lowepower.com
20512608Sjason@lowepower.com    // Given an address and machine type this queries the network to check
20612608Sjason@lowepower.com    // where it should be sent. In a real implementation, this might be fixed
20712608Sjason@lowepower.com    // at design time, but this function gives us flexibility at runtime.
20812608Sjason@lowepower.com    // For example, if you have multiple memory channels, this function will
20912608Sjason@lowepower.com    // tell you which addresses to send to which memory controller.
21012608Sjason@lowepower.com    MachineID mapAddressToMachine(Addr addr, MachineType mtype);
21112608Sjason@lowepower.com
21212608Sjason@lowepower.com    // Convience function to look up the cache entry.
21312608Sjason@lowepower.com    // Needs a pointer so it will be a reference and can be updated in actions
21412608Sjason@lowepower.com    Entry getCacheEntry(Addr address), return_by_pointer="yes" {
21512608Sjason@lowepower.com        return static_cast(Entry, "pointer", cacheMemory.lookup(address));
21612608Sjason@lowepower.com    }
21712608Sjason@lowepower.com
21812608Sjason@lowepower.com    /*************************************************************************/
21912608Sjason@lowepower.com    // Functions that we need to define/override to use our specific structures
22012608Sjason@lowepower.com    // in this implementation.
22112608Sjason@lowepower.com
22212608Sjason@lowepower.com    // Required function for getting the current state of the block.
22312608Sjason@lowepower.com    // This is called from the transition to know which transition to execute
22412608Sjason@lowepower.com    State getState(TBE tbe, Entry cache_entry, Addr addr) {
22512608Sjason@lowepower.com        // The TBE state will override the state in cache memory, if valid
22612608Sjason@lowepower.com        if (is_valid(tbe)) { return tbe.TBEState; }
22712608Sjason@lowepower.com        // Next, if the cache entry is valid, it holds the state
22812608Sjason@lowepower.com        else if (is_valid(cache_entry)) { return cache_entry.CacheState; }
22912608Sjason@lowepower.com        // If the block isn't present, then it's state must be I.
23012608Sjason@lowepower.com        else { return State:I; }
23112608Sjason@lowepower.com    }
23212608Sjason@lowepower.com
23312608Sjason@lowepower.com
23412608Sjason@lowepower.com    // Required function for setting the current state of the block.
23512608Sjason@lowepower.com    // This is called from the transition to set the ending state.
23612608Sjason@lowepower.com    // Needs to set both the TBE and the cache entry state.
23712608Sjason@lowepower.com    // This is also called when transitioning to I so it's possible the TBE and/
23812608Sjason@lowepower.com    // or the cache_entry is invalid.
23912608Sjason@lowepower.com    void setState(TBE tbe, Entry cache_entry, Addr addr, State state) {
24012608Sjason@lowepower.com      if (is_valid(tbe)) { tbe.TBEState := state; }
24112608Sjason@lowepower.com      if (is_valid(cache_entry)) { cache_entry.CacheState := state; }
24212608Sjason@lowepower.com    }
24312608Sjason@lowepower.com
24412608Sjason@lowepower.com    // Required function to override. Used for functional access to know where
24512608Sjason@lowepower.com    // the valid data is. NOTE: L1Cache_State_to_permission is automatically
24612608Sjason@lowepower.com    // created based on the access permissions in the state_declaration.
24712608Sjason@lowepower.com    // This is mangled by both the MachineType and the name of the state
24812608Sjason@lowepower.com    // declaration ("State" in this case)
24912608Sjason@lowepower.com    AccessPermission getAccessPermission(Addr addr) {
25012608Sjason@lowepower.com        TBE tbe := TBEs[addr];
25112608Sjason@lowepower.com        if(is_valid(tbe)) {
25212608Sjason@lowepower.com            return L1Cache_State_to_permission(tbe.TBEState);
25312608Sjason@lowepower.com        }
25412608Sjason@lowepower.com
25512608Sjason@lowepower.com        Entry cache_entry := getCacheEntry(addr);
25612608Sjason@lowepower.com        if(is_valid(cache_entry)) {
25712608Sjason@lowepower.com            return L1Cache_State_to_permission(cache_entry.CacheState);
25812608Sjason@lowepower.com        }
25912608Sjason@lowepower.com
26012608Sjason@lowepower.com        return AccessPermission:NotPresent;
26112608Sjason@lowepower.com    }
26212608Sjason@lowepower.com
26312608Sjason@lowepower.com    // Required function to override. Like above function, but sets thte state.
26412608Sjason@lowepower.com    void setAccessPermission(Entry cache_entry, Addr addr, State state) {
26512608Sjason@lowepower.com        if (is_valid(cache_entry)) {
26612608Sjason@lowepower.com            cache_entry.changePermission(L1Cache_State_to_permission(state));
26712608Sjason@lowepower.com        }
26812608Sjason@lowepower.com    }
26912608Sjason@lowepower.com
27012608Sjason@lowepower.com    // Required function to override for functionally reading/writing data.
27112608Sjason@lowepower.com    // NOTE: testAndRead/Write defined in src/mem/ruby/slicc_interface/Util.hh
27212608Sjason@lowepower.com    void functionalRead(Addr addr, Packet *pkt) {
27312608Sjason@lowepower.com        TBE tbe := TBEs[addr];
27412608Sjason@lowepower.com        if(is_valid(tbe)) {
27512608Sjason@lowepower.com            testAndRead(addr, tbe.DataBlk, pkt);
27612608Sjason@lowepower.com        } else {
27712608Sjason@lowepower.com            testAndRead(addr, getCacheEntry(addr).DataBlk, pkt);
27812608Sjason@lowepower.com        }
27912608Sjason@lowepower.com    }
28012608Sjason@lowepower.com
28112608Sjason@lowepower.com    int functionalWrite(Addr addr, Packet *pkt) {
28212608Sjason@lowepower.com        TBE tbe := TBEs[addr];
28312608Sjason@lowepower.com        if(is_valid(tbe)) {
28412608Sjason@lowepower.com            if (testAndWrite(addr, tbe.DataBlk, pkt)) {
28512608Sjason@lowepower.com                return 1;
28612608Sjason@lowepower.com            } else {
28712608Sjason@lowepower.com                return 0;
28812608Sjason@lowepower.com            }
28912608Sjason@lowepower.com        } else {
29012608Sjason@lowepower.com            if (testAndWrite(addr, getCacheEntry(addr).DataBlk, pkt)) {
29112608Sjason@lowepower.com                return 1;
29212608Sjason@lowepower.com            } else {
29312608Sjason@lowepower.com                return 0;
29412608Sjason@lowepower.com            }
29512608Sjason@lowepower.com        }
29612608Sjason@lowepower.com    }
29712608Sjason@lowepower.com
29812608Sjason@lowepower.com    /*************************************************************************/
29912608Sjason@lowepower.com    // Input/output network definitions
30012608Sjason@lowepower.com
30112608Sjason@lowepower.com    // Output ports. This defines the message types that will flow ocross the
30212608Sjason@lowepower.com    // output buffers as defined above. These must be "to" networks.
30312608Sjason@lowepower.com    // "request_out" is the name we'll use later to send requests.
30412608Sjason@lowepower.com    // "RequestMsg" is the message type we will send (see MSI-msg.sm)
30512608Sjason@lowepower.com    // "requestToDir" is the name of the MessageBuffer declared above that
30612608Sjason@lowepower.com    //      we are sending these requests out of.
30712608Sjason@lowepower.com    out_port(request_out, RequestMsg, requestToDir);
30812608Sjason@lowepower.com    out_port(response_out, ResponseMsg, responseToDirOrSibling);
30912608Sjason@lowepower.com
31012608Sjason@lowepower.com    // Input ports. The order here is/(can be) important. The code in each
31112608Sjason@lowepower.com    // in_port is executed in the order specified in this file (or by the rank
31212608Sjason@lowepower.com    // parameter). Thus, we must sort these based on the network priority.
31312608Sjason@lowepower.com    // In this cache, the order is responses from other caches, forwards, then
31412608Sjason@lowepower.com    // requests from the CPU.
31512608Sjason@lowepower.com
31612608Sjason@lowepower.com    // Like the out_port above
31712608Sjason@lowepower.com    // "response_in" is the name we'll use later when we refer to this port
31812608Sjason@lowepower.com    // "ResponseMsg" is the type of message we expect on this port
31912608Sjason@lowepower.com    // "responseFromDirOrSibling" is the name of the buffer this in_port is
32012608Sjason@lowepower.com    // connected to for responses from other caches and the directory.
32112608Sjason@lowepower.com    in_port(response_in, ResponseMsg, responseFromDirOrSibling) {
32212608Sjason@lowepower.com        // NOTE: You have to check to make sure the message buffer has a valid
32312608Sjason@lowepower.com        // message at the head. The code in in_port is executed either way.
32412608Sjason@lowepower.com        if (response_in.isReady(clockEdge())) {
32512608Sjason@lowepower.com            // Peek is a special function. Any code inside a peek statement has
32612608Sjason@lowepower.com            // a special variable declared and populated: in_msg. This contains
32712608Sjason@lowepower.com            // the message (of type RequestMsg in this case) at the head.
32812608Sjason@lowepower.com            // "forward_in" is the port we want to peek into
32912608Sjason@lowepower.com            // "RequestMsg" is the type of message we expect.
33012608Sjason@lowepower.com            peek(response_in, ResponseMsg) {
33112608Sjason@lowepower.com                // Grab the entry and tbe if they exist.
33212608Sjason@lowepower.com                Entry cache_entry := getCacheEntry(in_msg.addr);
33312608Sjason@lowepower.com                TBE tbe := TBEs[in_msg.addr];
33412608Sjason@lowepower.com                // The TBE better exist since this is a response and we need to
33512608Sjason@lowepower.com                // be able to check the remaining acks.
33612608Sjason@lowepower.com                assert(is_valid(tbe));
33712608Sjason@lowepower.com
33812608Sjason@lowepower.com                // If it's from the directory...
33912608Sjason@lowepower.com                if (machineIDToMachineType(in_msg.Sender) ==
34012608Sjason@lowepower.com                            MachineType:Directory) {
34112608Sjason@lowepower.com                    if (in_msg.Type != CoherenceResponseType:Data) {
34212608Sjason@lowepower.com                        error("Directory should only reply with data");
34312608Sjason@lowepower.com                    }
34412608Sjason@lowepower.com                    // Take the in_msg acks and add (sub) the Acks we've seen.
34512608Sjason@lowepower.com                    // The InvAck will decrement the acks we're waiting for in
34612608Sjason@lowepower.com                    // tbe.AcksOutstanding to below 0 if we haven't gotten the
34712608Sjason@lowepower.com                    // dir resp yet. So, if this is 0 we don't need to wait
34812608Sjason@lowepower.com                    assert(in_msg.Acks + tbe.AcksOutstanding >= 0);
34912608Sjason@lowepower.com                    if (in_msg.Acks + tbe.AcksOutstanding == 0) {
35012608Sjason@lowepower.com                        trigger(Event:DataDirNoAcks, in_msg.addr, cache_entry,
35112608Sjason@lowepower.com                                tbe);
35212608Sjason@lowepower.com                    } else {
35312608Sjason@lowepower.com                        // If it's not 0, then we need to wait for more acks
35412608Sjason@lowepower.com                        // and we'll trigger LastInvAck later.
35512608Sjason@lowepower.com                        trigger(Event:DataDirAcks, in_msg.addr, cache_entry,
35612608Sjason@lowepower.com                                tbe);
35712608Sjason@lowepower.com                    }
35812608Sjason@lowepower.com                } else {
35912608Sjason@lowepower.com                    // This is from another cache.
36012608Sjason@lowepower.com                    if (in_msg.Type == CoherenceResponseType:Data) {
36112608Sjason@lowepower.com                        trigger(Event:DataOwner, in_msg.addr, cache_entry,
36212608Sjason@lowepower.com                                tbe);
36312608Sjason@lowepower.com                    } else if (in_msg.Type == CoherenceResponseType:InvAck) {
36412608Sjason@lowepower.com                        DPRINTF(RubySlicc, "Got inv ack. %d left\n",
36512608Sjason@lowepower.com                                tbe.AcksOutstanding);
36612608Sjason@lowepower.com                        if (tbe.AcksOutstanding == 1) {
36712608Sjason@lowepower.com                            // If there is exactly one ack remaining then we
36812608Sjason@lowepower.com                            // know it is the last ack.
36912608Sjason@lowepower.com                            trigger(Event:LastInvAck, in_msg.addr, cache_entry,
37012608Sjason@lowepower.com                                    tbe);
37112608Sjason@lowepower.com                        } else {
37212608Sjason@lowepower.com                            trigger(Event:InvAck, in_msg.addr, cache_entry,
37312608Sjason@lowepower.com                                    tbe);
37412608Sjason@lowepower.com                        }
37512608Sjason@lowepower.com                    } else {
37612608Sjason@lowepower.com                        error("Unexpected response from other cache");
37712608Sjason@lowepower.com                    }
37812608Sjason@lowepower.com                }
37912608Sjason@lowepower.com            }
38012608Sjason@lowepower.com        }
38112608Sjason@lowepower.com    }
38212608Sjason@lowepower.com
38312608Sjason@lowepower.com    // Forward requests for other caches.
38412608Sjason@lowepower.com    in_port(forward_in, RequestMsg, forwardFromDir) {
38512608Sjason@lowepower.com        if (forward_in.isReady(clockEdge())) {
38612608Sjason@lowepower.com            peek(forward_in, RequestMsg) {
38712608Sjason@lowepower.com                // Grab the entry and tbe if they exist.
38812608Sjason@lowepower.com                Entry cache_entry := getCacheEntry(in_msg.addr);
38912608Sjason@lowepower.com                TBE tbe := TBEs[in_msg.addr];
39012608Sjason@lowepower.com
39112608Sjason@lowepower.com                if (in_msg.Type == CoherenceRequestType:GetS) {
39212608Sjason@lowepower.com                    // This is a special function that will trigger a
39312608Sjason@lowepower.com                    // transition (as defined below). It *must* have these
39412608Sjason@lowepower.com                    // parameters.
39512608Sjason@lowepower.com                    trigger(Event:FwdGetS, in_msg.addr, cache_entry, tbe);
39612608Sjason@lowepower.com                } else if (in_msg.Type == CoherenceRequestType:GetM) {
39712608Sjason@lowepower.com                    trigger(Event:FwdGetM, in_msg.addr, cache_entry, tbe);
39812608Sjason@lowepower.com                } else if (in_msg.Type == CoherenceRequestType:Inv) {
39912608Sjason@lowepower.com                    trigger(Event:Inv, in_msg.addr, cache_entry, tbe);
40012608Sjason@lowepower.com                } else if (in_msg.Type == CoherenceRequestType:PutAck) {
40112608Sjason@lowepower.com                    trigger(Event:PutAck, in_msg.addr, cache_entry, tbe);
40212608Sjason@lowepower.com                } else {
40312608Sjason@lowepower.com                    error("Unexpected forward message!");
40412608Sjason@lowepower.com                }
40512608Sjason@lowepower.com            }
40612608Sjason@lowepower.com        }
40712608Sjason@lowepower.com    }
40812608Sjason@lowepower.com
40912608Sjason@lowepower.com    // The "mandatory queue" is the port/queue from the CPU or other processor.
41012608Sjason@lowepower.com    // This is *always* a RubyRequest
41112608Sjason@lowepower.com    in_port(mandatory_in, RubyRequest, mandatoryQueue) {
41212608Sjason@lowepower.com        if (mandatory_in.isReady(clockEdge())) {
41312608Sjason@lowepower.com            // Block all requests if there is already an outstanding request
41412608Sjason@lowepower.com            // that has the same line address. This is unblocked when we
41512608Sjason@lowepower.com            // finally respond to the request.
41612608Sjason@lowepower.com            peek(mandatory_in, RubyRequest, block_on="LineAddress") {
41712608Sjason@lowepower.com                // NOTE: Using LineAddress here to promote smaller requests to
41812608Sjason@lowepower.com                // full cache block requests.
41912608Sjason@lowepower.com                Entry cache_entry := getCacheEntry(in_msg.LineAddress);
42012608Sjason@lowepower.com                TBE tbe := TBEs[in_msg.LineAddress];
42112608Sjason@lowepower.com                // If there isn't a matching entry and no room in the cache,
42212608Sjason@lowepower.com                // then we need to find a victim.
42312608Sjason@lowepower.com                if (is_invalid(cache_entry) &&
42412608Sjason@lowepower.com                        cacheMemory.cacheAvail(in_msg.LineAddress) == false ) {
42512608Sjason@lowepower.com                    // make room for the block
42612608Sjason@lowepower.com                    // The "cacheProbe" function looks at the cache set for
42712608Sjason@lowepower.com                    // the address and queries the replacement protocol for
42812608Sjason@lowepower.com                    // the address to replace. It returns the address to repl.
42912608Sjason@lowepower.com                    Addr addr := cacheMemory.cacheProbe(in_msg.LineAddress);
43012608Sjason@lowepower.com                    Entry victim_entry := getCacheEntry(addr);
43112608Sjason@lowepower.com                    TBE victim_tbe := TBEs[addr];
43212608Sjason@lowepower.com                    trigger(Event:Replacement, addr, victim_entry, victim_tbe);
43312608Sjason@lowepower.com                } else {
43412608Sjason@lowepower.com                    if (in_msg.Type == RubyRequestType:LD ||
43512608Sjason@lowepower.com                            in_msg.Type == RubyRequestType:IFETCH) {
43612608Sjason@lowepower.com                        trigger(Event:Load, in_msg.LineAddress, cache_entry,
43712608Sjason@lowepower.com                                tbe);
43812608Sjason@lowepower.com                    } else if (in_msg.Type == RubyRequestType:ST) {
43912608Sjason@lowepower.com                        trigger(Event:Store, in_msg.LineAddress, cache_entry,
44012608Sjason@lowepower.com                                tbe);
44112608Sjason@lowepower.com                    } else {
44212608Sjason@lowepower.com                        error("Unexpected type from processor");
44312608Sjason@lowepower.com                    }
44412608Sjason@lowepower.com                }
44512608Sjason@lowepower.com            }
44612608Sjason@lowepower.com        }
44712608Sjason@lowepower.com    }
44812608Sjason@lowepower.com
44912608Sjason@lowepower.com
45012608Sjason@lowepower.com    /*************************************************************************/
45112608Sjason@lowepower.com    // Below are all of the actions that might be taken on a transition.
45212608Sjason@lowepower.com
45312608Sjason@lowepower.com    // Each actions has a name, a shorthand, and a description.
45412608Sjason@lowepower.com    // The shorthand is used when generating the HTML tables for the protocol.
45512608Sjason@lowepower.com    // "\" in the shorthand cause that letter to be bold. Underscores insert a
45612608Sjason@lowepower.com    // space, ^ makes the rest of the letters superscript.
45712608Sjason@lowepower.com    // The description is also shown in the HTML table when clicked
45812608Sjason@lowepower.com
45912608Sjason@lowepower.com    // The first set of actions are things we will do to interact with the
46012608Sjason@lowepower.com    // rest of the system. Things like sending requests/responses.
46112608Sjason@lowepower.com
46212608Sjason@lowepower.com    // Action blocks define a number of implicit variables that are useful.
46312608Sjason@lowepower.com    // These variables come straight from the trigger() call in the in_port
46412608Sjason@lowepower.com    // blocks.
46512608Sjason@lowepower.com    // address: The address passed in the trigger (usually the in_msg.addr,
46612608Sjason@lowepower.com    //          though it can be different. E.g., on a replacement it is the
46712608Sjason@lowepower.com    //          victim address).
46812608Sjason@lowepower.com    // cache_entry: The cache entry passed in the trigger call
46912608Sjason@lowepower.com    // tbe: The TBE passed in the trigger call
47012608Sjason@lowepower.com    action(sendGetS, 'gS', desc="Send GetS to the directory") {
47112608Sjason@lowepower.com        // The syntax for enqueue is a lot like peek. Instead of populating
47212608Sjason@lowepower.com        // in_msg, enqueue has an out_msg reference. Whatever you set on out_msg
47312608Sjason@lowepower.com        // is sent through the out port specified.  "request_out" is the port
47412608Sjason@lowepower.com        // we're sending the message out of "RequestMsg" is the type of message
47512608Sjason@lowepower.com        // we're sending "1" is the latency (in cycles) the port waits before
47612608Sjason@lowepower.com        // sending the message.
47712608Sjason@lowepower.com        enqueue(request_out, RequestMsg, 1) {
47812608Sjason@lowepower.com            out_msg.addr := address;
47912608Sjason@lowepower.com            // This type is defined in MSI-msg.sm for this protocol.
48012608Sjason@lowepower.com            out_msg.Type := CoherenceRequestType:GetS;
48112608Sjason@lowepower.com            // The destination may change depending on the address striping
48212608Sjason@lowepower.com            // across different directories, so query the network.
48312608Sjason@lowepower.com            out_msg.Destination.add(mapAddressToMachine(address,
48412608Sjason@lowepower.com                                    MachineType:Directory));
48514184Sgabeblack@google.com            // See mem/ruby/protocol/RubySlicc_Exports.sm for possible sizes.
48612608Sjason@lowepower.com            out_msg.MessageSize := MessageSizeType:Control;
48712608Sjason@lowepower.com            // Set that the reqeustor is this machine so we get the response.
48812608Sjason@lowepower.com            out_msg.Requestor := machineID;
48912608Sjason@lowepower.com        }
49012608Sjason@lowepower.com    }
49112608Sjason@lowepower.com
49212608Sjason@lowepower.com    action(sendGetM, "gM", desc="Send GetM to the directory") {
49312608Sjason@lowepower.com        enqueue(request_out, RequestMsg, 1) {
49412608Sjason@lowepower.com            out_msg.addr := address;
49512608Sjason@lowepower.com            out_msg.Type := CoherenceRequestType:GetM;
49612608Sjason@lowepower.com            out_msg.Destination.add(mapAddressToMachine(address,
49712608Sjason@lowepower.com                                    MachineType:Directory));
49812608Sjason@lowepower.com            out_msg.MessageSize := MessageSizeType:Control;
49912608Sjason@lowepower.com            out_msg.Requestor := machineID;
50012608Sjason@lowepower.com        }
50112608Sjason@lowepower.com    }
50212608Sjason@lowepower.com
50312608Sjason@lowepower.com    // NOTE: Clean evict. Required to keep the directory state up-to-date
50412608Sjason@lowepower.com    action(sendPutS, "pS", desc="Send PutS to the directory") {
50512608Sjason@lowepower.com        enqueue(request_out, RequestMsg, 1) {
50612608Sjason@lowepower.com            out_msg.addr := address;
50712608Sjason@lowepower.com            out_msg.Type := CoherenceRequestType:PutS;
50812608Sjason@lowepower.com            out_msg.Destination.add(mapAddressToMachine(address,
50912608Sjason@lowepower.com                                    MachineType:Directory));
51012608Sjason@lowepower.com            out_msg.MessageSize := MessageSizeType:Control;
51112608Sjason@lowepower.com            out_msg.Requestor := machineID;
51212608Sjason@lowepower.com        }
51312608Sjason@lowepower.com    }
51412608Sjason@lowepower.com
51512608Sjason@lowepower.com    action(sendPutM, "pM", desc="Send putM+data to the directory") {
51612608Sjason@lowepower.com        enqueue(request_out, RequestMsg, 1) {
51712608Sjason@lowepower.com            out_msg.addr := address;
51812608Sjason@lowepower.com            out_msg.Type := CoherenceRequestType:PutM;
51912608Sjason@lowepower.com            out_msg.Destination.add(mapAddressToMachine(address,
52012608Sjason@lowepower.com                                    MachineType:Directory));
52112608Sjason@lowepower.com            out_msg.DataBlk := cache_entry.DataBlk;
52212608Sjason@lowepower.com            out_msg.MessageSize := MessageSizeType:Data;
52312608Sjason@lowepower.com            out_msg.Requestor := machineID;
52412608Sjason@lowepower.com        }
52512608Sjason@lowepower.com    }
52612608Sjason@lowepower.com
52712608Sjason@lowepower.com    action(sendCacheDataToReq, "cdR", desc="Send cache data to requestor") {
52812608Sjason@lowepower.com        // We have to peek into the request to see who to send to.
52912608Sjason@lowepower.com        // If we are in both the peek and the enqueue block then we have access
53012608Sjason@lowepower.com        // to both in_msg and out_msg.
53112608Sjason@lowepower.com        assert(is_valid(cache_entry));
53212608Sjason@lowepower.com        peek(forward_in, RequestMsg) {
53312608Sjason@lowepower.com            enqueue(response_out, ResponseMsg, 1) {
53412608Sjason@lowepower.com                out_msg.addr := address;
53512608Sjason@lowepower.com                out_msg.Type := CoherenceResponseType:Data;
53612608Sjason@lowepower.com                out_msg.Destination.add(in_msg.Requestor);
53712608Sjason@lowepower.com                out_msg.DataBlk := cache_entry.DataBlk;
53812608Sjason@lowepower.com                out_msg.MessageSize := MessageSizeType:Data;
53912608Sjason@lowepower.com                out_msg.Sender := machineID;
54012608Sjason@lowepower.com            }
54112608Sjason@lowepower.com        }
54212608Sjason@lowepower.com    }
54312608Sjason@lowepower.com
54412608Sjason@lowepower.com    action(sendCacheDataToDir, "cdD", desc="Send the cache data to the dir") {
54512608Sjason@lowepower.com        enqueue(response_out, ResponseMsg, 1) {
54612608Sjason@lowepower.com            out_msg.addr := address;
54712608Sjason@lowepower.com            out_msg.Type := CoherenceResponseType:Data;
54812608Sjason@lowepower.com            out_msg.Destination.add(mapAddressToMachine(address,
54912608Sjason@lowepower.com                                    MachineType:Directory));
55012608Sjason@lowepower.com            out_msg.DataBlk := cache_entry.DataBlk;
55112608Sjason@lowepower.com            out_msg.MessageSize := MessageSizeType:Data;
55212608Sjason@lowepower.com            out_msg.Sender := machineID;
55312608Sjason@lowepower.com        }
55412608Sjason@lowepower.com    }
55512608Sjason@lowepower.com
55612608Sjason@lowepower.com    action(sendInvAcktoReq, "iaR", desc="Send inv-ack to requestor") {
55712608Sjason@lowepower.com        peek(forward_in, RequestMsg) {
55812608Sjason@lowepower.com            enqueue(response_out, ResponseMsg, 1) {
55912608Sjason@lowepower.com                out_msg.addr := address;
56012608Sjason@lowepower.com                out_msg.Type := CoherenceResponseType:InvAck;
56112608Sjason@lowepower.com                out_msg.Destination.add(in_msg.Requestor);
56212608Sjason@lowepower.com                out_msg.DataBlk := cache_entry.DataBlk;
56312608Sjason@lowepower.com                out_msg.MessageSize := MessageSizeType:Control;
56412608Sjason@lowepower.com                out_msg.Sender := machineID;
56512608Sjason@lowepower.com            }
56612608Sjason@lowepower.com        }
56712608Sjason@lowepower.com    }
56812608Sjason@lowepower.com
56912608Sjason@lowepower.com    action(decrAcks, "da", desc="Decrement the number of acks") {
57012608Sjason@lowepower.com        assert(is_valid(tbe));
57112608Sjason@lowepower.com        tbe.AcksOutstanding := tbe.AcksOutstanding - 1;
57212608Sjason@lowepower.com        // This annotates the protocol trace
57312608Sjason@lowepower.com        APPEND_TRANSITION_COMMENT("Acks: ");
57412608Sjason@lowepower.com        APPEND_TRANSITION_COMMENT(tbe.AcksOutstanding);
57512608Sjason@lowepower.com    }
57612608Sjason@lowepower.com
57712608Sjason@lowepower.com    action(storeAcks, "sa", desc="Store the needed acks to the TBE") {
57812608Sjason@lowepower.com        assert(is_valid(tbe));
57912608Sjason@lowepower.com        peek(response_in, ResponseMsg) {
58012608Sjason@lowepower.com            tbe.AcksOutstanding := in_msg.Acks + tbe.AcksOutstanding;
58112608Sjason@lowepower.com        }
58212608Sjason@lowepower.com        assert(tbe.AcksOutstanding > 0);
58312608Sjason@lowepower.com    }
58412608Sjason@lowepower.com
58512608Sjason@lowepower.com    // Responses to CPU requests (e.g., hits and store acks)
58612608Sjason@lowepower.com
58712608Sjason@lowepower.com    action(loadHit, "Lh", desc="Load hit") {
58812608Sjason@lowepower.com        assert(is_valid(cache_entry));
58912608Sjason@lowepower.com        // Set this entry as the most recently used for the replacement policy
59012608Sjason@lowepower.com        cacheMemory.setMRU(cache_entry);
59112608Sjason@lowepower.com        // Send the data back to the sequencer/CPU. NOTE: False means it was
59212608Sjason@lowepower.com        // not an "external hit", but hit in this local cache.
59312608Sjason@lowepower.com        sequencer.readCallback(address, cache_entry.DataBlk, false);
59412608Sjason@lowepower.com    }
59512608Sjason@lowepower.com
59612608Sjason@lowepower.com    action(externalLoadHit, "xLh", desc="External load hit (was a miss)") {
59712608Sjason@lowepower.com        assert(is_valid(cache_entry));
59812608Sjason@lowepower.com        peek(response_in, ResponseMsg) {
59912608Sjason@lowepower.com            cacheMemory.setMRU(cache_entry);
60012608Sjason@lowepower.com            // Forward the type of machine that responded to this request
60112608Sjason@lowepower.com            // E.g., another cache or the directory. This is used for tracking
60212608Sjason@lowepower.com            // statistics.
60312608Sjason@lowepower.com            sequencer.readCallback(address, cache_entry.DataBlk, true,
60412608Sjason@lowepower.com                                   machineIDToMachineType(in_msg.Sender));
60512608Sjason@lowepower.com        }
60612608Sjason@lowepower.com    }
60712608Sjason@lowepower.com
60812608Sjason@lowepower.com    action(storeHit, "Sh", desc="Store hit") {
60912608Sjason@lowepower.com        assert(is_valid(cache_entry));
61012608Sjason@lowepower.com        cacheMemory.setMRU(cache_entry);
61112608Sjason@lowepower.com        // The same as the read callback above.
61212608Sjason@lowepower.com        sequencer.writeCallback(address, cache_entry.DataBlk, false);
61312608Sjason@lowepower.com    }
61412608Sjason@lowepower.com
61512608Sjason@lowepower.com    action(externalStoreHit, "xSh", desc="External store hit (was a miss)") {
61612608Sjason@lowepower.com        assert(is_valid(cache_entry));
61712608Sjason@lowepower.com        peek(response_in, ResponseMsg) {
61812608Sjason@lowepower.com            cacheMemory.setMRU(cache_entry);
61912608Sjason@lowepower.com            sequencer.writeCallback(address, cache_entry.DataBlk, true,
62012608Sjason@lowepower.com                                   // Note: this could be the last ack.
62112608Sjason@lowepower.com                                   machineIDToMachineType(in_msg.Sender));
62212608Sjason@lowepower.com        }
62312608Sjason@lowepower.com    }
62412608Sjason@lowepower.com
62512608Sjason@lowepower.com    action(forwardEviction, "e", desc="sends eviction notification to CPU") {
62612608Sjason@lowepower.com        if (send_evictions) {
62712608Sjason@lowepower.com            sequencer.evictionCallback(address);
62812608Sjason@lowepower.com        }
62912608Sjason@lowepower.com    }
63012608Sjason@lowepower.com
63112608Sjason@lowepower.com    // Cache management actions
63212608Sjason@lowepower.com
63312608Sjason@lowepower.com    action(allocateCacheBlock, "a", desc="Allocate a cache block") {
63412608Sjason@lowepower.com        assert(is_invalid(cache_entry));
63512608Sjason@lowepower.com        assert(cacheMemory.cacheAvail(address));
63612608Sjason@lowepower.com        // Create a new entry and update cache_entry to the new entry
63712608Sjason@lowepower.com        set_cache_entry(cacheMemory.allocate(address, new Entry));
63812608Sjason@lowepower.com    }
63912608Sjason@lowepower.com
64012608Sjason@lowepower.com    action(deallocateCacheBlock, "d", desc="Deallocate a cache block") {
64112608Sjason@lowepower.com        assert(is_valid(cache_entry));
64212608Sjason@lowepower.com        cacheMemory.deallocate(address);
64312608Sjason@lowepower.com        // clear the cache_entry variable (now it's invalid)
64412608Sjason@lowepower.com        unset_cache_entry();
64512608Sjason@lowepower.com    }
64612608Sjason@lowepower.com
64712608Sjason@lowepower.com    action(writeDataToCache, "wd", desc="Write data to the cache") {
64812608Sjason@lowepower.com        peek(response_in, ResponseMsg) {
64912608Sjason@lowepower.com            assert(is_valid(cache_entry));
65012608Sjason@lowepower.com            cache_entry.DataBlk := in_msg.DataBlk;
65112608Sjason@lowepower.com        }
65212608Sjason@lowepower.com    }
65312608Sjason@lowepower.com
65412608Sjason@lowepower.com    action(allocateTBE, "aT", desc="Allocate TBE") {
65512608Sjason@lowepower.com        assert(is_invalid(tbe));
65612608Sjason@lowepower.com        TBEs.allocate(address);
65712608Sjason@lowepower.com        // this updates the tbe variable for other actions
65812608Sjason@lowepower.com        set_tbe(TBEs[address]);
65912608Sjason@lowepower.com    }
66012608Sjason@lowepower.com
66112608Sjason@lowepower.com    action(deallocateTBE, "dT", desc="Deallocate TBE") {
66212608Sjason@lowepower.com        assert(is_valid(tbe));
66312608Sjason@lowepower.com        TBEs.deallocate(address);
66412608Sjason@lowepower.com        // this makes the tbe varible invalid
66512608Sjason@lowepower.com        unset_tbe();
66612608Sjason@lowepower.com    }
66712608Sjason@lowepower.com
66812608Sjason@lowepower.com    // Queue management actions
66912608Sjason@lowepower.com
67012608Sjason@lowepower.com    action(popMandatoryQueue, "pQ", desc="Pop the mandatory queue") {
67112608Sjason@lowepower.com        mandatory_in.dequeue(clockEdge());
67212608Sjason@lowepower.com    }
67312608Sjason@lowepower.com
67412608Sjason@lowepower.com    action(popResponseQueue, "pR", desc="Pop the response queue") {
67512608Sjason@lowepower.com        response_in.dequeue(clockEdge());
67612608Sjason@lowepower.com    }
67712608Sjason@lowepower.com
67812608Sjason@lowepower.com    action(popForwardQueue, "pF", desc="Pop the forward queue") {
67912608Sjason@lowepower.com        forward_in.dequeue(clockEdge());
68012608Sjason@lowepower.com    }
68112608Sjason@lowepower.com
68212608Sjason@lowepower.com    // Stalling actions
68312608Sjason@lowepower.com
68412608Sjason@lowepower.com    action(stall, "z", desc="Stall the incoming request") {
68512608Sjason@lowepower.com        // Do nothing. However, the transition must have some action to be
68612608Sjason@lowepower.com        // valid which is why this is needed.
68712608Sjason@lowepower.com        // NOTE: There are other more complicated but higher performing stalls
68812608Sjason@lowepower.com        // in Ruby like recycle() or stall_and_wait.
68912608Sjason@lowepower.com        // z_stall stalls everything in the queue behind this request.
69012608Sjason@lowepower.com    }
69112608Sjason@lowepower.com
69212608Sjason@lowepower.com
69312608Sjason@lowepower.com    /*************************************************************************/
69412608Sjason@lowepower.com    // These are the transition definition. These are simply each cell in the
69512608Sjason@lowepower.com    // table from Sorin et al. These are mostly in upper-left to bottom-right
69612608Sjason@lowepower.com    // order
69712608Sjason@lowepower.com
69812608Sjason@lowepower.com    // Each transtiion has (up to) 3 parameters, the current state, the
69912608Sjason@lowepower.com    // triggering event and the final state. Thus, the below transition reads
70012608Sjason@lowepower.com    // "Move from state I on a Load event to state IS_D". Below are other
70112608Sjason@lowepower.com    // examples of transition statements.
70212608Sjason@lowepower.com    // Within the transition statement is a set of action to take during the
70312608Sjason@lowepower.com    // transition. These actions are executed atomically (i.e., all or nothing)
70412608Sjason@lowepower.com    transition(I, Load, IS_D) {
70512608Sjason@lowepower.com        // Make sure there is room in the cache to put the block whenever the
70612608Sjason@lowepower.com        // miss returns. Otherwise we could deadlock.
70712608Sjason@lowepower.com        allocateCacheBlock;
70812608Sjason@lowepower.com        // We may need to track acks for this block and only the TBE holds an
70912608Sjason@lowepower.com        // ack count. Thus, we need to allocate both a TBE and cache block.
71012608Sjason@lowepower.com        allocateTBE;
71112608Sjason@lowepower.com        // Actually send the request to the directory
71212608Sjason@lowepower.com        sendGetS;
71312608Sjason@lowepower.com        // Since we have handled this request on the mandatory queue, we can pop
71412608Sjason@lowepower.com        popMandatoryQueue;
71512608Sjason@lowepower.com    }
71612608Sjason@lowepower.com
71712608Sjason@lowepower.com    transition(I, Store, IM_AD) {
71812608Sjason@lowepower.com        allocateCacheBlock;
71912608Sjason@lowepower.com        allocateTBE;
72012608Sjason@lowepower.com        sendGetM;
72112608Sjason@lowepower.com        popMandatoryQueue;
72212608Sjason@lowepower.com    }
72312608Sjason@lowepower.com
72412608Sjason@lowepower.com    // You can use {} to specify multiple states or events for which the
72512608Sjason@lowepower.com    // transition applies. For instance, below. If we are in IS_D, then on any
72612608Sjason@lowepower.com    // of the following Events (Load, Store, Replacement, Inv) we should stall
72712608Sjason@lowepower.com    // When there is no third parameter to transition, it means that we want
72812608Sjason@lowepower.com    // to stay in the initial state.
72912608Sjason@lowepower.com    transition(IS_D, {Load, Store, Replacement, Inv}) {
73012608Sjason@lowepower.com        stall;
73112608Sjason@lowepower.com    }
73212608Sjason@lowepower.com
73312608Sjason@lowepower.com    // Similarly, on either DataDirNoAcks or DataOwner we should go to S
73412608Sjason@lowepower.com    transition(IS_D, {DataDirNoAcks, DataOwner}, S) {
73512608Sjason@lowepower.com        writeDataToCache;
73612608Sjason@lowepower.com        deallocateTBE;
73712608Sjason@lowepower.com        externalLoadHit;
73812608Sjason@lowepower.com        popResponseQueue;
73912608Sjason@lowepower.com    }
74012608Sjason@lowepower.com
74112608Sjason@lowepower.com    transition({IM_AD, IM_A}, {Load, Store, Replacement, FwdGetS, FwdGetM}) {
74212608Sjason@lowepower.com        stall;
74312608Sjason@lowepower.com    }
74412608Sjason@lowepower.com
74512608Sjason@lowepower.com    transition({IM_AD, SM_AD}, {DataDirNoAcks, DataOwner}, M) {
74612608Sjason@lowepower.com        writeDataToCache;
74712608Sjason@lowepower.com        deallocateTBE;
74812608Sjason@lowepower.com        externalStoreHit;
74912608Sjason@lowepower.com        popResponseQueue;
75012608Sjason@lowepower.com    }
75112608Sjason@lowepower.com
75212608Sjason@lowepower.com    transition(IM_AD, DataDirAcks, IM_A) {
75312608Sjason@lowepower.com        writeDataToCache;
75412608Sjason@lowepower.com        storeAcks;
75512608Sjason@lowepower.com        popResponseQueue;
75612608Sjason@lowepower.com    }
75712608Sjason@lowepower.com
75812608Sjason@lowepower.com    transition({IM_AD, IM_A, SM_AD, SM_A}, InvAck) {
75912608Sjason@lowepower.com        decrAcks;
76012608Sjason@lowepower.com        popResponseQueue;
76112608Sjason@lowepower.com    }
76212608Sjason@lowepower.com
76312608Sjason@lowepower.com    transition({IM_A, SM_A}, LastInvAck, M) {
76412608Sjason@lowepower.com        deallocateTBE;
76512608Sjason@lowepower.com        externalStoreHit;
76612608Sjason@lowepower.com        popResponseQueue;
76712608Sjason@lowepower.com    }
76812608Sjason@lowepower.com
76912608Sjason@lowepower.com    transition({S, SM_AD, SM_A, M}, Load) {
77012608Sjason@lowepower.com        loadHit;
77112608Sjason@lowepower.com        popMandatoryQueue;
77212608Sjason@lowepower.com    }
77312608Sjason@lowepower.com
77412608Sjason@lowepower.com    transition(S, Store, SM_AD) {
77512608Sjason@lowepower.com        allocateTBE;
77612608Sjason@lowepower.com        sendGetM;
77712608Sjason@lowepower.com        popMandatoryQueue;
77812608Sjason@lowepower.com    }
77912608Sjason@lowepower.com
78012608Sjason@lowepower.com    transition(S, Replacement, SI_A) {
78112608Sjason@lowepower.com        sendPutS;
78212608Sjason@lowepower.com    }
78312608Sjason@lowepower.com
78412608Sjason@lowepower.com    transition(S, Inv, I) {
78512608Sjason@lowepower.com        sendInvAcktoReq;
78612608Sjason@lowepower.com        forwardEviction;
78712608Sjason@lowepower.com        deallocateCacheBlock;
78812608Sjason@lowepower.com        popForwardQueue;
78912608Sjason@lowepower.com    }
79012608Sjason@lowepower.com
79112608Sjason@lowepower.com    transition({SM_AD, SM_A}, {Store, Replacement, FwdGetS, FwdGetM}) {
79212608Sjason@lowepower.com        stall;
79312608Sjason@lowepower.com    }
79412608Sjason@lowepower.com
79512608Sjason@lowepower.com    transition(SM_AD, Inv, IM_AD) {
79612608Sjason@lowepower.com        sendInvAcktoReq;
79712608Sjason@lowepower.com        popForwardQueue;
79812608Sjason@lowepower.com    }
79912608Sjason@lowepower.com
80012608Sjason@lowepower.com    transition(SM_AD, DataDirAcks, SM_A) {
80112608Sjason@lowepower.com        writeDataToCache;
80212608Sjason@lowepower.com        storeAcks;
80312608Sjason@lowepower.com        popResponseQueue;
80412608Sjason@lowepower.com    }
80512608Sjason@lowepower.com
80612608Sjason@lowepower.com    transition(M, Store) {
80712608Sjason@lowepower.com        storeHit;
80812608Sjason@lowepower.com        forwardEviction;
80912608Sjason@lowepower.com        popMandatoryQueue;
81012608Sjason@lowepower.com    }
81112608Sjason@lowepower.com
81212608Sjason@lowepower.com    transition(M, Replacement, MI_A) {
81312608Sjason@lowepower.com        sendPutM;
81412608Sjason@lowepower.com    }
81512608Sjason@lowepower.com
81612608Sjason@lowepower.com    transition(M, FwdGetS, S) {
81712608Sjason@lowepower.com        sendCacheDataToReq;
81812608Sjason@lowepower.com        sendCacheDataToDir;
81912608Sjason@lowepower.com        popForwardQueue;
82012608Sjason@lowepower.com    }
82112608Sjason@lowepower.com
82212608Sjason@lowepower.com    transition(M, FwdGetM, I) {
82312608Sjason@lowepower.com        sendCacheDataToReq;
82412608Sjason@lowepower.com        deallocateCacheBlock;
82512608Sjason@lowepower.com        popForwardQueue;
82612608Sjason@lowepower.com    }
82712608Sjason@lowepower.com
82812608Sjason@lowepower.com    transition({MI_A, SI_A, II_A}, {Load, Store, Replacement}) {
82912608Sjason@lowepower.com        stall;
83012608Sjason@lowepower.com    }
83112608Sjason@lowepower.com
83212608Sjason@lowepower.com    transition(MI_A, FwdGetS, SI_A) {
83312608Sjason@lowepower.com        sendCacheDataToReq;
83412608Sjason@lowepower.com        sendCacheDataToDir;
83512608Sjason@lowepower.com        popForwardQueue;
83612608Sjason@lowepower.com    }
83712608Sjason@lowepower.com
83812608Sjason@lowepower.com    transition(MI_A, FwdGetM, II_A) {
83912608Sjason@lowepower.com        sendCacheDataToReq;
84012608Sjason@lowepower.com        popForwardQueue;
84112608Sjason@lowepower.com    }
84212608Sjason@lowepower.com
84312608Sjason@lowepower.com    transition({MI_A, SI_A, II_A}, PutAck, I) {
84412608Sjason@lowepower.com        deallocateCacheBlock;
84512608Sjason@lowepower.com        popForwardQueue;
84612608Sjason@lowepower.com    }
84712608Sjason@lowepower.com
84812608Sjason@lowepower.com    transition(SI_A, Inv, II_A) {
84912608Sjason@lowepower.com        sendInvAcktoReq;
85012608Sjason@lowepower.com        popForwardQueue;
85112608Sjason@lowepower.com    }
85212608Sjason@lowepower.com
85312608Sjason@lowepower.com}
854