ruby: message buffers: significant changes
[gem5.git] / src / mem / protocol / MI_example-cache.sm
index 915a0eb999114ff891faffd1aa512ab02baf2db5..ee774f4c24bdfec3e7455eca45d08a154bd143e3 100644 (file)
@@ -1,25 +1,60 @@
+/*
+ * Copyright (c) 2009-2012 Mark D. Hill and David A. Wood
+ * Copyright (c) 2010-2012 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
 
 machine(L1Cache, "MI Example L1 Cache")
-: int cache_response_latency,
-  int issue_latency
+    : Sequencer * sequencer;
+      CacheMemory * cacheMemory;
+      Cycles cache_response_latency := 12;
+      Cycles issue_latency := 2;
+      bool send_evictions;
+
+      // NETWORK BUFFERS
+      MessageBuffer * requestFromCache, network="To", virtual_network="2",
+            ordered="true", vnet_type="request";
+      MessageBuffer * responseFromCache, network="To", virtual_network="4",
+            ordered="true", vnet_type="response";
+
+      MessageBuffer * forwardToCache, network="From", virtual_network="3",
+            ordered="true", vnet_type="forward";
+      MessageBuffer * responseToCache, network="From", virtual_network="4",
+            ordered="true", vnet_type="response";
 {
-
-  // NETWORK BUFFERS
-  MessageBuffer requestFromCache, network="To", virtual_network="0", ordered="true";
-  MessageBuffer responseFromCache, network="To", virtual_network="1", ordered="true";
-
-  MessageBuffer forwardToCache, network="From", virtual_network="2", ordered="true";
-  MessageBuffer responseToCache, network="From", virtual_network="1", ordered="true";
-
   // STATES
-  enumeration(State, desc="Cache states") {
-    I, desc="Not Present/Invalid";
-    II, desc="Not Present/Invalid, issued PUT";
-    M,  desc="Modified";
-    MI,  desc="Modified, issued PUT";
+  state_declaration(State, desc="Cache states") {
+    I, AccessPermission:Invalid, desc="Not Present/Invalid";
+    II, AccessPermission:Busy, desc="Not Present/Invalid, issued PUT";
+    M, AccessPermission:Read_Write, desc="Modified";
+    MI, AccessPermission:Busy, desc="Modified, issued PUT";
+    MII, AccessPermission:Busy, desc="Modified, issued PUTX, received nack";
 
-    IS,  desc="Issued request for LOAD/IFETCH";
-    IM,  desc="Issued request for STORE/ATOMIC";
+    IS, AccessPermission:Busy, desc="Issued request for LOAD/IFETCH";
+    IM, AccessPermission:Busy, desc="Issued request for STORE/ATOMIC";
   }
 
   // EVENTS
@@ -43,7 +78,6 @@ machine(L1Cache, "MI Example L1 Cache")
   // STRUCTURE DEFINITIONS
 
   MessageBuffer mandatoryQueue, ordered="false";
-  Sequencer sequencer, factory='RubySystem::getSequencer(m_cfg["sequencer"])';
 
   // CacheEntry
   structure(Entry, desc="...", interface="AbstractCacheEntry") {
@@ -52,25 +86,13 @@ machine(L1Cache, "MI Example L1 Cache")
     DataBlock DataBlk,       desc="Data in the block";
   }
 
-
-  external_type(CacheMemory) {
-    bool cacheAvail(Address);
-    Address cacheProbe(Address);
-    void allocate(Address, Entry);
-    void deallocate(Address);
-    Entry lookup(Address);
-    void changePermission(Address, AccessPermission);
-    bool isTagPresent(Address);
-    void profileMiss(CacheMsg);
-  }
-
   // TBE fields
   structure(TBE, desc="...") {
     State TBEState,          desc="Transient state";
     DataBlock DataBlk,       desc="data for the block, required for concurrent writebacks";
   }
 
-  external_type(TBETable) {
+  structure(TBETable, external="yes") {
     TBE lookup(Address);
     void allocate(Address);
     void deallocate(Address);
@@ -79,56 +101,84 @@ machine(L1Cache, "MI Example L1 Cache")
 
 
   // STRUCTURES
+  TBETable TBEs, template="<L1Cache_TBE>", constructor="m_number_of_TBEs";
 
-  CacheMemory cacheMemory, factory='RubySystem::getCache(m_cfg["cache"])';
-
-  TBETable TBEs, template_hack="<L1Cache_TBE>";
-
+  // PROTOTYPES
+  void set_cache_entry(AbstractCacheEntry a);
+  void unset_cache_entry();
+  void set_tbe(TBE b);
+  void unset_tbe();
+  void profileMsgDelay(int virtualNetworkType, Cycles b);
 
+  Entry getCacheEntry(Address address), return_by_pointer="yes" {
+    return static_cast(Entry, "pointer", cacheMemory.lookup(address));
+  }
 
   // FUNCTIONS
-  Event mandatory_request_type_to_event(CacheRequestType type) {
-   if (type == CacheRequestType:LD) {
+  Event mandatory_request_type_to_event(RubyRequestType type) {
+   if (type == RubyRequestType:LD) {
       return Event:Load;
-    } else if (type == CacheRequestType:IFETCH) {
+    } else if (type == RubyRequestType:IFETCH) {
       return Event:Ifetch;
-    } else if ((type == CacheRequestType:ST) || (type == CacheRequestType:ATOMIC)) {
+    } else if ((type == RubyRequestType:ST) || (type == RubyRequestType:ATOMIC)) {
       return Event:Store;
     } else {
-      error("Invalid CacheRequestType");
+      error("Invalid RubyRequestType");
     }
   }
 
+  State getState(TBE tbe, Entry cache_entry, Address addr) {
 
-  State getState(Address addr) {
-
-    if (TBEs.isPresent(addr)) {
-      return TBEs[addr].TBEState;
+    if (is_valid(tbe)) {
+      return tbe.TBEState;
     }
-    else if (cacheMemory.isTagPresent(addr)) {
-      return cacheMemory[addr].CacheState;
+    else if (is_valid(cache_entry)) {
+      return cache_entry.CacheState;
     }
     else {
       return State:I;
     }
   }
 
-  void setState(Address addr, State state) {
+  void setState(TBE tbe, Entry cache_entry, Address addr, State state) {
 
-    if (TBEs.isPresent(addr)) {
-      TBEs[addr].TBEState := state;
+    if (is_valid(tbe)) {
+      tbe.TBEState := state;
     }
 
-    if (cacheMemory.isTagPresent(addr)) {
-      cacheMemory[addr].CacheState := state;
-      if (state == State:M) {
-        cacheMemory.changePermission(addr, AccessPermission:Read_Write);
-      } else {
-        cacheMemory.changePermission(addr, AccessPermission:Invalid);
-      }
+    if (is_valid(cache_entry)) {
+      cache_entry.CacheState := state;
+    }
+  }
+
+  AccessPermission getAccessPermission(Address addr) {
+    TBE tbe := TBEs[addr];
+    if(is_valid(tbe)) {
+      return L1Cache_State_to_permission(tbe.TBEState);
+    }
+
+    Entry cache_entry := getCacheEntry(addr);
+    if(is_valid(cache_entry)) {
+      return L1Cache_State_to_permission(cache_entry.CacheState);
+    }
+
+    return AccessPermission:NotPresent;
+  }
+
+  void setAccessPermission(Entry cache_entry, Address addr, State state) {
+    if (is_valid(cache_entry)) {
+      cache_entry.changePermission(L1Cache_State_to_permission(state));
     }
   }
 
+  DataBlock getDataBlock(Address addr), return_by_ref="yes" {
+    TBE tbe := TBEs[addr];
+    if(is_valid(tbe)) {
+      return tbe.DataBlk;
+    }
+
+    return getCacheEntry(addr).DataBlk;
+  }
 
   // NETWORK PORTS
 
@@ -137,18 +187,22 @@ machine(L1Cache, "MI Example L1 Cache")
 
   in_port(forwardRequestNetwork_in, RequestMsg, forwardToCache) {
     if (forwardRequestNetwork_in.isReady()) {
-      peek(forwardRequestNetwork_in, RequestMsg) {
+      peek(forwardRequestNetwork_in, RequestMsg, block_on="Addr") {
+
+        Entry cache_entry := getCacheEntry(in_msg.Addr);
+        TBE tbe := TBEs[in_msg.Addr];
+
         if (in_msg.Type == CoherenceRequestType:GETX) {
-          trigger(Event:Fwd_GETX, in_msg.Address);
+          trigger(Event:Fwd_GETX, in_msg.Addr, cache_entry, tbe);
         }
         else if (in_msg.Type == CoherenceRequestType:WB_ACK) {
-          trigger(Event:Writeback_Ack, in_msg.Address);
+          trigger(Event:Writeback_Ack, in_msg.Addr, cache_entry, tbe);
         }
         else if (in_msg.Type == CoherenceRequestType:WB_NACK) {
-          trigger(Event:Writeback_Nack, in_msg.Address);
+          trigger(Event:Writeback_Nack, in_msg.Addr, cache_entry, tbe);
         }
         else if (in_msg.Type == CoherenceRequestType:INV) {
-          trigger(Event:Inv, in_msg.Address);
+          trigger(Event:Inv, in_msg.Addr, cache_entry, tbe);
         }
         else {
           error("Unexpected message");
@@ -159,9 +213,13 @@ machine(L1Cache, "MI Example L1 Cache")
 
   in_port(responseNetwork_in, ResponseMsg, responseToCache) {
     if (responseNetwork_in.isReady()) {
-      peek(responseNetwork_in, ResponseMsg) {
+      peek(responseNetwork_in, ResponseMsg, block_on="Addr") {
+
+        Entry cache_entry := getCacheEntry(in_msg.Addr);
+        TBE tbe := TBEs[in_msg.Addr];
+
         if (in_msg.Type == CoherenceResponseType:DATA) {
-          trigger(Event:Data, in_msg.Address);
+          trigger(Event:Data, in_msg.Addr, cache_entry, tbe);
         }
         else {
           error("Unexpected message");
@@ -171,18 +229,21 @@ machine(L1Cache, "MI Example L1 Cache")
   }
 
     // Mandatory Queue
-  in_port(mandatoryQueue_in, CacheMsg, mandatoryQueue, desc="...") {
+  in_port(mandatoryQueue_in, RubyRequest, mandatoryQueue, desc="...") {
     if (mandatoryQueue_in.isReady()) {
-      peek(mandatoryQueue_in, CacheMsg) {
+      peek(mandatoryQueue_in, RubyRequest, block_on="LineAddress") {
 
-
-        if (cacheMemory.isTagPresent(in_msg.LineAddress) == false &&
+        Entry cache_entry := getCacheEntry(in_msg.LineAddress);
+        if (is_invalid(cache_entry) &&
             cacheMemory.cacheAvail(in_msg.LineAddress) == false ) {
           // make room for the block
-          trigger(Event:Replacement, cacheMemory.cacheProbe(in_msg.LineAddress));
+          trigger(Event:Replacement, cacheMemory.cacheProbe(in_msg.LineAddress),
+                  getCacheEntry(cacheMemory.cacheProbe(in_msg.LineAddress)),
+                  TBEs[cacheMemory.cacheProbe(in_msg.LineAddress)]);
         }
         else {
-          trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.LineAddress);
+          trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.LineAddress,
+                  cache_entry, TBEs[in_msg.LineAddress]);
         }
       }
     }
@@ -191,8 +252,8 @@ machine(L1Cache, "MI Example L1 Cache")
   // ACTIONS
 
   action(a_issueRequest, "a", desc="Issue a request") {
-    enqueue(requestNetwork_out, RequestMsg, latency=issue_latency) {
-    out_msg.Address := address;
+    enqueue(requestNetwork_out, RequestMsg, issue_latency) {
+    out_msg.Addr := address;
       out_msg.Type := CoherenceRequestType:GETX;
       out_msg.Requestor := machineID;
       out_msg.Destination.add(map_Address_to_Directory(address));
@@ -201,25 +262,26 @@ machine(L1Cache, "MI Example L1 Cache")
   }
 
   action(b_issuePUT, "b", desc="Issue a PUT request") {
-    enqueue(requestNetwork_out, RequestMsg, latency=issue_latency) {
-      out_msg.Address := address;
+    enqueue(requestNetwork_out, RequestMsg, issue_latency) {
+      assert(is_valid(cache_entry));
+      out_msg.Addr := address;
       out_msg.Type := CoherenceRequestType:PUTX;
       out_msg.Requestor := machineID;
       out_msg.Destination.add(map_Address_to_Directory(address));
-      out_msg.DataBlk := cacheMemory[address].DataBlk;
+      out_msg.DataBlk := cache_entry.DataBlk;
       out_msg.MessageSize := MessageSizeType:Data;
     }
   }
 
-
   action(e_sendData, "e", desc="Send data from cache to requestor") {
     peek(forwardRequestNetwork_in, RequestMsg) {
-      enqueue(responseNetwork_out, ResponseMsg, latency=cache_response_latency) {
-        out_msg.Address := address;
+      enqueue(responseNetwork_out, ResponseMsg, cache_response_latency) {
+        assert(is_valid(cache_entry));
+        out_msg.Addr := address;
         out_msg.Type := CoherenceResponseType:DATA;
         out_msg.Sender := machineID;
         out_msg.Destination.add(in_msg.Requestor);
-        out_msg.DataBlk := cacheMemory[address].DataBlk;
+        out_msg.DataBlk := cache_entry.DataBlk;
         out_msg.MessageSize := MessageSizeType:Response_Data;
       }
     }
@@ -227,27 +289,29 @@ machine(L1Cache, "MI Example L1 Cache")
 
   action(ee_sendDataFromTBE, "\e", desc="Send data from TBE to requestor") {
     peek(forwardRequestNetwork_in, RequestMsg) {
-      enqueue(responseNetwork_out, ResponseMsg, latency=cache_response_latency) {
-        out_msg.Address := address;
+      enqueue(responseNetwork_out, ResponseMsg, cache_response_latency) {
+        assert(is_valid(tbe));
+        out_msg.Addr := address;
         out_msg.Type := CoherenceResponseType:DATA;
         out_msg.Sender := machineID;
         out_msg.Destination.add(in_msg.Requestor);
-        out_msg.DataBlk := TBEs[address].DataBlk;
+        out_msg.DataBlk := tbe.DataBlk;
         out_msg.MessageSize := MessageSizeType:Response_Data;
       }
     }
   }
 
-
   action(i_allocateL1CacheBlock, "i", desc="Allocate a cache block") {
-    if (cacheMemory.isTagPresent(address) == false) {
-      cacheMemory.allocate(address, new Entry);
+    if (is_valid(cache_entry)) {
+    } else {
+      set_cache_entry(cacheMemory.allocate(address, new Entry));
     }
   }
 
   action(h_deallocateL1CacheBlock, "h", desc="deallocate a cache block") {
-    if (cacheMemory.isTagPresent(address) == true) {
+    if (is_valid(cache_entry)) {
       cacheMemory.deallocate(address);
+      unset_cache_entry();
     }
   }
 
@@ -256,48 +320,79 @@ machine(L1Cache, "MI Example L1 Cache")
   }
 
   action(n_popResponseQueue, "n", desc="Pop the response queue") {
-    profileMsgDelay(1, responseNetwork_in.dequeue_getDelayCycles());
+    profileMsgDelay(1, responseNetwork_in.dequeue());
   }
 
   action(o_popForwardedRequestQueue, "o", desc="Pop the forwarded request queue") {
-    profileMsgDelay(2, forwardRequestNetwork_in.dequeue_getDelayCycles());
+    profileMsgDelay(2, forwardRequestNetwork_in.dequeue());
   }
 
-  action(p_profileMiss, "p", desc="Profile cache miss") {
-    peek(mandatoryQueue_in, CacheMsg) {
-      cacheMemory.profileMiss(in_msg);
-    }
+  action(p_profileMiss, "pi", desc="Profile cache miss") {
+      ++cacheMemory.demand_misses;
+  }
+
+  action(p_profileHit, "ph", desc="Profile cache miss") {
+      ++cacheMemory.demand_hits;
   }
 
   action(r_load_hit, "r", desc="Notify sequencer the load completed.") {
-    DEBUG_EXPR(cacheMemory[address].DataBlk);
-    sequencer.readCallback(address, cacheMemory[address].DataBlk);
+    assert(is_valid(cache_entry));
+    DPRINTF(RubySlicc,"%s\n", cache_entry.DataBlk);
+    sequencer.readCallback(address, cache_entry.DataBlk, false);
+  }
+
+  action(rx_load_hit, "rx", desc="External load completed.") {
+    peek(responseNetwork_in, ResponseMsg) {
+      assert(is_valid(cache_entry));
+      DPRINTF(RubySlicc,"%s\n", cache_entry.DataBlk);
+      sequencer.readCallback(address, cache_entry.DataBlk, true,
+                             machineIDToMachineType(in_msg.Sender));
+    }
   }
 
   action(s_store_hit, "s", desc="Notify sequencer that store completed.") {
-    DEBUG_EXPR(cacheMemory[address].DataBlk);
-    sequencer.writeCallback(address, cacheMemory[address].DataBlk);
+    assert(is_valid(cache_entry));
+    DPRINTF(RubySlicc,"%s\n", cache_entry.DataBlk);
+    sequencer.writeCallback(address, cache_entry.DataBlk, false);
   }
 
+  action(sx_store_hit, "sx", desc="External store completed.") {
+    peek(responseNetwork_in, ResponseMsg) {
+      assert(is_valid(cache_entry));
+      DPRINTF(RubySlicc,"%s\n", cache_entry.DataBlk);
+      sequencer.writeCallback(address, cache_entry.DataBlk, true,
+                              machineIDToMachineType(in_msg.Sender));
+    }
+  }
 
   action(u_writeDataToCache, "u", desc="Write data to the cache") {
     peek(responseNetwork_in, ResponseMsg) {
-      cacheMemory[address].DataBlk := in_msg.DataBlk;
+      assert(is_valid(cache_entry));
+      cache_entry.DataBlk := in_msg.DataBlk;
     }
   }
 
+  action(forward_eviction_to_cpu, "\cc", desc="sends eviction information to the processor") {
+    if (send_evictions) {
+      DPRINTF(RubySlicc, "Sending invalidation for %s to the CPU\n", address);
+      sequencer.evictionCallback(address);
+    }
+  }
 
   action(v_allocateTBE, "v", desc="Allocate TBE") {
     TBEs.allocate(address);
+    set_tbe(TBEs[address]);
   }
 
-
   action(w_deallocateTBE, "w", desc="Deallocate TBE") {
     TBEs.deallocate(address);
+    unset_tbe();
   }
 
   action(x_copyDataFromCacheToTBE, "x", desc="Copy data from cache to TBE") {
-    TBEs[address].DataBlk := cacheMemory[address].DataBlk;
+    assert(is_valid(cache_entry));
+    assert(is_valid(tbe));
+    tbe.DataBlk := cache_entry.DataBlk;
   }
 
   action(z_stall, "z", desc="stall") {
@@ -306,7 +401,7 @@ machine(L1Cache, "MI Example L1 Cache")
 
   // TRANSITIONS
 
-  transition({IS, IM, MI, II}, {Load, Ifetch, Store, Replacement}) {
+  transition({IS, IM, MI, II, MII}, {Load, Ifetch, Store, Replacement}) {
     z_stall;
   }
 
@@ -320,11 +415,13 @@ machine(L1Cache, "MI Example L1 Cache")
 
   transition(M, Store) {
     s_store_hit;
+    p_profileHit;
     m_popMandatoryQueue;
   }
 
   transition(M, {Load, Ifetch}) {
     r_load_hit;
+    p_profileHit;
     m_popMandatoryQueue;
   }
 
@@ -350,20 +447,21 @@ machine(L1Cache, "MI Example L1 Cache")
 
   transition(IS, Data, M) {
     u_writeDataToCache;
-    r_load_hit;
+    rx_load_hit;
     w_deallocateTBE;
     n_popResponseQueue;
   }
 
   transition(IM, Data, M) {
     u_writeDataToCache;
-    s_store_hit;
+    sx_store_hit;
     w_deallocateTBE;
     n_popResponseQueue;
   }
 
   transition(M, Fwd_GETX, I) {
     e_sendData;
+    forward_eviction_to_cpu;
     o_popForwardedRequestQueue;
   }
 
@@ -375,6 +473,7 @@ machine(L1Cache, "MI Example L1 Cache")
      v_allocateTBE;
      b_issuePUT;
      x_copyDataFromCacheToTBE;
+     forward_eviction_to_cpu;
      h_deallocateL1CacheBlock;
   }
 
@@ -388,9 +487,18 @@ machine(L1Cache, "MI Example L1 Cache")
     o_popForwardedRequestQueue;
   }
 
+  transition(MI, Writeback_Nack, MII) {
+    o_popForwardedRequestQueue;
+  }
+
+  transition(MII, Fwd_GETX, I) {
+    ee_sendDataFromTBE;
+    w_deallocateTBE;
+    o_popForwardedRequestQueue;
+  }
+
   transition(II, Writeback_Nack, I) {
     w_deallocateTBE;
     o_popForwardedRequestQueue;
   }
 }
-