/*
 * Copyright (c) 2010-2015 Advanced Micro Devices, Inc.
 * All rights reserved.
 *
 * For use for simulation and test purposes only
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. 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.
 *
 * 3. Neither the name of the copyright holder 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 HOLDER 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.
 *
 * Author: Lisa Hsu
 */

machine(MachineType:CorePair, "CP-like Core Coherence")
 : Sequencer * sequencer;
   Sequencer * sequencer1;
   CacheMemory * L1Icache;
   CacheMemory * L1D0cache;
   CacheMemory * L1D1cache;
   CacheMemory * L2cache;
   int regionBufferNum;
   bool send_evictions := "False";
   Cycles issue_latency := 5;
   Cycles l2_hit_latency := 18;

  // BEGIN Core Buffers

  // To the Network
  MessageBuffer * requestFromCore, network="To", virtual_network="0", ordered="true", vnet_type="request";
  MessageBuffer * responseFromCore, network="To", virtual_network="2", ordered="false", vnet_type="response";
  MessageBuffer * unblockFromCore, network="To", virtual_network="4", ordered="false", vnet_type="unblock";

  // From the Network
  MessageBuffer * probeToCore, network="From", virtual_network="0", ordered="false", vnet_type="request";
  MessageBuffer * responseToCore, network="From", virtual_network="2", ordered="false", vnet_type="response";

  MessageBuffer * mandatoryQueue, ordered="false";
  MessageBuffer * triggerQueue, ordered="true";

  // END Core Buffers

{
  // BEGIN STATES
  state_declaration(State, desc="Cache states", default="CorePair_State_I") {

    I, AccessPermission:Invalid, desc="Invalid";
    S, AccessPermission:Read_Only, desc="Shared";
    E0, AccessPermission:Read_Write, desc="Exclusive with Cluster 0 ownership";
    E1, AccessPermission:Read_Write, desc="Exclusive with Cluster 1 ownership";
    Es, AccessPermission:Read_Write, desc="Exclusive in core";
    O, AccessPermission:Read_Only, desc="Owner state in core, both clusters and other cores may be sharing line";
    Ms, AccessPermission:Read_Write, desc="Modified in core, both clusters may be sharing line";
    M0, AccessPermission:Read_Write, desc="Modified with cluster ownership";
    M1, AccessPermission:Read_Write, desc="Modified with cluster ownership";

    // Transient States
    I_M0, AccessPermission:Busy, desc="Invalid, issued RdBlkM, have not seen response yet";
    I_M1, AccessPermission:Busy, desc="Invalid, issued RdBlkM, have not seen response yet";
    I_M0M1, AccessPermission:Busy, desc="Was in I_M0, got a store request from other cluster as well";
    I_M1M0, AccessPermission:Busy, desc="Was in I_M1, got a store request from other cluster as well";
    I_M0Ms, AccessPermission:Busy, desc="Was in I_M0, got a load request from other cluster as well";
    I_M1Ms, AccessPermission:Busy, desc="Was in I_M1, got a load request from other cluster as well";
    I_E0S, AccessPermission:Busy, desc="Invalid, issued RdBlk, have not seen response yet";
    I_E1S, AccessPermission:Busy, desc="Invalid, issued RdBlk, have not seen response yet";
    I_ES, AccessPermission:Busy, desc="S_F got hit by invalidating probe, RdBlk response needs to go to both clusters";

    IF_E0S, AccessPermission:Busy, desc="something got hit with Probe Invalidate, now just I_E0S but expecting a L2_to_L1D0 trigger, just drop when receive";
    IF_E1S, AccessPermission:Busy, desc="something got hit with Probe Invalidate, now just I_E1S but expecting a L2_to_L1D1 trigger, just drop when receive";
    IF_ES, AccessPermission:Busy, desc="same, but waiting for two fills";
    IF0_ES, AccessPermission:Busy, desc="same, but waiting for two fills, got one";
    IF1_ES, AccessPermission:Busy, desc="same, but waiting for two fills, got one";
    F_S0, AccessPermission:Busy, desc="same, but going to S0 when trigger received";
    F_S1, AccessPermission:Busy, desc="same, but going to S1 when trigger received";

    ES_I, AccessPermission:Read_Only, desc="L2 replacement, waiting for clean writeback ack";
    MO_I, AccessPermission:Read_Only, desc="L2 replacement, waiting for dirty writeback ack";
    MO_S0, AccessPermission:Read_Only, desc="M/O got Ifetch Miss, must write back first, then send RdBlkS";
    MO_S1, AccessPermission:Read_Only, desc="M/O got Ifetch Miss, must write back first, then send RdBlkS";
    S_F0, AccessPermission:Read_Only,  desc="Shared, filling L1";
    S_F1, AccessPermission:Read_Only,  desc="Shared, filling L1";
    S_F, AccessPermission:Read_Only,   desc="Shared, filling L1";
    O_F0, AccessPermission:Read_Only,  desc="Owned, filling L1";
    O_F1, AccessPermission:Read_Only,  desc="Owned, filling L1";
    O_F,  AccessPermission:Read_Only,  desc="Owned, filling L1";
    Si_F0, AccessPermission:Read_Only, desc="Shared, filling icache";
    Si_F1, AccessPermission:Read_Only, desc="Shared, filling icache";
    S_M0, AccessPermission:Read_Only, desc="Shared, issued CtoD, have not seen response yet";
    S_M1, AccessPermission:Read_Only, desc="Shared, issued CtoD, have not seen response yet";
    O_M0, AccessPermission:Read_Only, desc="Shared, issued CtoD, have not seen response yet";
    O_M1, AccessPermission:Read_Only, desc="Shared, issued CtoD, have not seen response yet";
    S0, AccessPermission:Busy, desc="RdBlkS on behalf of cluster 0, waiting for response";
    S1, AccessPermission:Busy, desc="RdBlkS on behalf of cluster 1, waiting for response";

    Es_F0, AccessPermission:Read_Write, desc="Es, Cluster read, filling";
    Es_F1, AccessPermission:Read_Write, desc="Es, Cluster read, filling";
    Es_F, AccessPermission:Read_Write,  desc="Es, other cluster read, filling";
    E0_F, AccessPermission:Read_Write, desc="E0, cluster read, filling";
    E1_F, AccessPermission:Read_Write, desc="...";
    E0_Es, AccessPermission:Read_Write, desc="...";
    E1_Es, AccessPermission:Read_Write, desc="...";
    Ms_F0, AccessPermission:Read_Write, desc="...";
    Ms_F1, AccessPermission:Read_Write, desc="...";
    Ms_F, AccessPermission:Read_Write,  desc="...";
    M0_F, AccessPermission:Read_Write, desc="...";
    M0_Ms, AccessPermission:Read_Write, desc="...";
    M1_F, AccessPermission:Read_Write, desc="...";
    M1_Ms, AccessPermission:Read_Write, desc="...";

    I_C, AccessPermission:Invalid, desc="Invalid, but waiting for WBAck from NB from canceled writeback";
    S0_C, AccessPermission:Busy, desc="MO_S0 hit by invalidating probe, waiting for WBAck form NB for canceled WB";
    S1_C, AccessPermission:Busy, desc="MO_S1 hit by invalidating probe, waiting for WBAck form NB for canceled WB";
    S_C, AccessPermission:Busy, desc="S*_C got NB_AckS, still waiting for WBAck";

  } // END STATES

  // BEGIN EVENTS
  enumeration(Event, desc="CP Events") {
    // CP Initiated events
    C0_Load_L1miss,            desc="Cluster 0 load, L1 missed";
    C0_Load_L1hit,             desc="Cluster 0 load, L1 hit";
    C1_Load_L1miss,            desc="Cluster 1 load L1 missed";
    C1_Load_L1hit,             desc="Cluster 1 load L1 hit";
    Ifetch0_L1hit,             desc="Instruction fetch, hit in the L1";
    Ifetch1_L1hit,             desc="Instruction fetch, hit in the L1";
    Ifetch0_L1miss,            desc="Instruction fetch, missed in the L1";
    Ifetch1_L1miss,            desc="Instruction fetch, missed in the L1";
    C0_Store_L1miss,           desc="Cluster 0 store missed in L1";
    C0_Store_L1hit,            desc="Cluster 0 store hit in L1";
    C1_Store_L1miss,           desc="Cluster 1 store missed in L1";
    C1_Store_L1hit,            desc="Cluster 1 store hit in L1";
    // NB Initiated events
    NB_AckS,             desc="NB Ack to Core Request";
    NB_AckM,             desc="NB Ack to Core Request";
    NB_AckE,             desc="NB Ack to Core Request";

    NB_AckWB,            desc="NB Ack for writeback";

    // Memory System initiatied events
    L1I_Repl,           desc="Replace address from L1I"; // Presumed clean
    L1D0_Repl,           desc="Replace address from L1D0"; // Presumed clean
    L1D1_Repl,           desc="Replace address from L1D1"; // Presumed clean
    L2_Repl,            desc="Replace address from L2";

    L2_to_L1D0,           desc="L1 fill from L2";
    L2_to_L1D1,           desc="L1 fill from L2";
    L2_to_L1I,           desc="L1 fill from L2";

    // Probe Events
    PrbInvData,         desc="probe, return O or M data";
    PrbInvDataDemand,     desc="probe, return O or M data. Demand request";
    PrbInv,             desc="probe, no need for data";
    PrbShrData,         desc="probe downgrade, return O or M data";
    PrbShrDataDemand,     desc="probe downgrade, return O or M data. Demand request";
    ForceRepl,          desc="probe from r-buf. Act as though a repl";
    ForceDowngrade,     desc="probe from r-buf. Act as though a repl";

  }  // END EVENTS

  enumeration(RequestType, desc="To communicate stats from transitions to recordStats") {
    L1D0DataArrayRead,    desc="Read the data array";
    L1D0DataArrayWrite,   desc="Write the data array";
    L1D0TagArrayRead,     desc="Read the data array";
    L1D0TagArrayWrite,    desc="Write the data array";
    L1D1DataArrayRead,    desc="Read the data array";
    L1D1DataArrayWrite,   desc="Write the data array";
    L1D1TagArrayRead,     desc="Read the data array";
    L1D1TagArrayWrite,    desc="Write the data array";
    L1IDataArrayRead,     desc="Read the data array";
    L1IDataArrayWrite,    desc="Write the data array";
    L1ITagArrayRead,      desc="Read the data array";
    L1ITagArrayWrite,     desc="Write the data array";
    L2DataArrayRead,      desc="Read the data array";
    L2DataArrayWrite,     desc="Write the data array";
    L2TagArrayRead,       desc="Read the data array";
    L2TagArrayWrite,      desc="Write the data array";
  }


  // BEGIN STRUCTURE DEFINITIONS


  // Cache Entry
  structure(Entry, desc="...", interface="AbstractCacheEntry") {
    State CacheState,           desc="cache state";
    bool Dirty,                 desc="Is the data dirty (diff than memory)?";
    DataBlock DataBlk,          desc="data for the block";
    bool FromL2, default="false", desc="block just moved from L2";
  }

  structure(TBE, desc="...") {
    State TBEState,             desc="Transient state";
    DataBlock DataBlk,       desc="data for the block, required for concurrent writebacks";
    bool Dirty,              desc="Is the data dirty (different than memory)?";
    int NumPendingMsgs,      desc="Number of acks/data messages that this processor is waiting for";
    bool Shared,             desc="Victim hit by shared probe";
    bool AckNeeded,          desc="True if need to ack r-dir";
   }

  structure(TBETable, external="yes") {
    TBE lookup(Addr);
    void allocate(Addr);
    void deallocate(Addr);
    bool isPresent(Addr);
  }

  TBETable TBEs, template="<CorePair_TBE>", constructor="m_number_of_TBEs";

  Tick clockEdge();
  Tick cyclesToTicks(Cycles c);

  void set_cache_entry(AbstractCacheEntry b);
  void unset_cache_entry();
  void set_tbe(TBE b);
  void unset_tbe();
  void wakeUpAllBuffers();
  void wakeUpBuffers(Addr a);
  Cycles curCycle();

  // END STRUCTURE DEFINITIONS

  // BEGIN INTERNAL FUNCTIONS

  MachineID getPeer(MachineID mach) {
    return createMachineID(MachineType:RegionBuffer, intToID(regionBufferNum));
  }

  bool addressInCore(Addr addr) {
    return (L2cache.isTagPresent(addr) || L1Icache.isTagPresent(addr) || L1D0cache.isTagPresent(addr) || L1D1cache.isTagPresent(addr));
  }

  Entry getCacheEntry(Addr address), return_by_pointer="yes" {
    Entry L2cache_entry := static_cast(Entry, "pointer", L2cache.lookup(address));
    return L2cache_entry;
  }

  DataBlock getDataBlock(Addr addr), return_by_ref="yes" {
    TBE tbe := TBEs.lookup(addr);
    if(is_valid(tbe)) {
      return tbe.DataBlk;
    } else {
      return getCacheEntry(addr).DataBlk;
    }
  }

  Entry getL1CacheEntry(Addr addr, int cluster), return_by_pointer="yes" {
    if (cluster == 0) {
      Entry L1D0_entry := static_cast(Entry, "pointer", L1D0cache.lookup(addr));
      return L1D0_entry;
    } else {
      Entry L1D1_entry := static_cast(Entry, "pointer", L1D1cache.lookup(addr));
      return L1D1_entry;
    }
  }

  Entry getICacheEntry(Addr addr), return_by_pointer="yes" {
    Entry c_entry := static_cast(Entry, "pointer", L1Icache.lookup(addr));
    return c_entry;
  }

  bool presentOrAvail2(Addr addr) {
    return L2cache.isTagPresent(addr) || L2cache.cacheAvail(addr);
  }

  bool presentOrAvailI(Addr addr) {
    return L1Icache.isTagPresent(addr) || L1Icache.cacheAvail(addr);
  }

  bool presentOrAvailD0(Addr addr) {
    return L1D0cache.isTagPresent(addr) || L1D0cache.cacheAvail(addr);
  }

  bool presentOrAvailD1(Addr addr) {
    return L1D1cache.isTagPresent(addr) || L1D1cache.cacheAvail(addr);
  }

  State getState(TBE tbe, Entry cache_entry, Addr addr) {
    if(is_valid(tbe)) {
      return tbe.TBEState;
    } else if (is_valid(cache_entry)) {
      return cache_entry.CacheState;
    }
    return State:I;
  }

  void setState(TBE tbe, Entry cache_entry, Addr addr, State state) {
    if (is_valid(tbe)) {
      tbe.TBEState := state;
    }

    if (is_valid(cache_entry)) {
      cache_entry.CacheState := state;
    }
  }

  AccessPermission getAccessPermission(Addr addr) {
    TBE tbe := TBEs.lookup(addr);
    if(is_valid(tbe)) {
      return CorePair_State_to_permission(tbe.TBEState);
    }

    Entry cache_entry := getCacheEntry(addr);
    if(is_valid(cache_entry)) {
      return CorePair_State_to_permission(cache_entry.CacheState);
    }

    return AccessPermission:NotPresent;
  }

  void functionalRead(Addr addr, Packet *pkt) {
    TBE tbe := TBEs.lookup(addr);
    if(is_valid(tbe)) {
      testAndRead(addr, tbe.DataBlk, pkt);
    } else {
      functionalMemoryRead(pkt);
    }
  }

  int functionalWrite(Addr addr, Packet *pkt) {
    int num_functional_writes := 0;

    TBE tbe := TBEs.lookup(addr);
    if(is_valid(tbe)) {
      num_functional_writes := num_functional_writes +
            testAndWrite(addr, tbe.DataBlk, pkt);
    }

    num_functional_writes := num_functional_writes + functionalMemoryWrite(pkt);
    return num_functional_writes;
  }

  bool isValid(Addr addr) {
      AccessPermission perm := getAccessPermission(addr);
      if (perm == AccessPermission:NotPresent ||
          perm == AccessPermission:Invalid ||
          perm == AccessPermission:Busy) {
          return false;
      } else {
          return true;
      }
  }

  void setAccessPermission(Entry cache_entry, Addr addr, State state) {
    if (is_valid(cache_entry)) {
      cache_entry.changePermission(CorePair_State_to_permission(state));
    }
  }

  MachineType testAndClearLocalHit(Entry cache_entry) {
    assert(is_valid(cache_entry));
    if (cache_entry.FromL2) {
      cache_entry.FromL2 := false;
      return MachineType:L2Cache;
    } else {
      return MachineType:L1Cache;
    }
  }

  void recordRequestType(RequestType request_type, Addr addr) {
    if (request_type == RequestType:L1D0DataArrayRead) {
      L1D0cache.recordRequestType(CacheRequestType:DataArrayRead, addr);
    } else if (request_type == RequestType:L1D0DataArrayWrite) {
      L1D0cache.recordRequestType(CacheRequestType:DataArrayWrite, addr);
    } else if (request_type == RequestType:L1D0TagArrayRead) {
      L1D0cache.recordRequestType(CacheRequestType:TagArrayRead, addr);
    } else if (request_type == RequestType:L1D0TagArrayWrite) {
      L1D0cache.recordRequestType(CacheRequestType:TagArrayWrite, addr);
    } else if (request_type == RequestType:L1D1DataArrayRead) {
      L1D1cache.recordRequestType(CacheRequestType:DataArrayRead, addr);
    } else if (request_type == RequestType:L1D1DataArrayWrite) {
      L1D1cache.recordRequestType(CacheRequestType:DataArrayWrite, addr);
    } else if (request_type == RequestType:L1D1TagArrayRead) {
      L1D1cache.recordRequestType(CacheRequestType:TagArrayRead, addr);
    } else if (request_type == RequestType:L1D1TagArrayWrite) {
      L1D1cache.recordRequestType(CacheRequestType:TagArrayWrite, addr);
    } else if (request_type == RequestType:L1IDataArrayRead) {
      L1Icache.recordRequestType(CacheRequestType:DataArrayRead, addr);
    } else if (request_type == RequestType:L1IDataArrayWrite) {
      L1Icache.recordRequestType(CacheRequestType:DataArrayWrite, addr);
    } else if (request_type == RequestType:L1ITagArrayRead) {
      L1Icache.recordRequestType(CacheRequestType:TagArrayRead, addr);
    } else if (request_type == RequestType:L1ITagArrayWrite) {
      L1Icache.recordRequestType(CacheRequestType:TagArrayWrite, addr);
    } else if (request_type == RequestType:L2DataArrayRead) {
      L2cache.recordRequestType(CacheRequestType:DataArrayRead, addr);
    } else if (request_type == RequestType:L2DataArrayWrite) {
      L2cache.recordRequestType(CacheRequestType:DataArrayWrite, addr);
    } else if (request_type == RequestType:L2TagArrayRead) {
      L2cache.recordRequestType(CacheRequestType:TagArrayRead, addr);
    } else if (request_type == RequestType:L2TagArrayWrite) {
      L2cache.recordRequestType(CacheRequestType:TagArrayWrite, addr);
    }
  }

  bool checkResourceAvailable(RequestType request_type, Addr addr) {
    if (request_type == RequestType:L2DataArrayRead) {
      return L2cache.checkResourceAvailable(CacheResourceType:DataArray, addr);
    } else if (request_type == RequestType:L2DataArrayWrite) {
      return L2cache.checkResourceAvailable(CacheResourceType:DataArray, addr);
    } else if (request_type == RequestType:L2TagArrayRead) {
      return L2cache.checkResourceAvailable(CacheResourceType:TagArray, addr);
    } else if (request_type == RequestType:L2TagArrayWrite) {
      return L2cache.checkResourceAvailable(CacheResourceType:TagArray, addr);
    } else if  (request_type == RequestType:L1D0DataArrayRead) {
      return L1D0cache.checkResourceAvailable(CacheResourceType:DataArray, addr);
    } else if  (request_type == RequestType:L1D0DataArrayWrite) {
      return L1D0cache.checkResourceAvailable(CacheResourceType:DataArray, addr);
    } else if (request_type == RequestType:L1D0TagArrayRead) {
      return L1D0cache.checkResourceAvailable(CacheResourceType:TagArray, addr);
    } else if (request_type == RequestType:L1D0TagArrayWrite) {
      return L1D0cache.checkResourceAvailable(CacheResourceType:TagArray, addr);
    } else if (request_type == RequestType:L1D1DataArrayRead) {
      return L1D1cache.checkResourceAvailable(CacheResourceType:DataArray, addr);
    } else if (request_type == RequestType:L1D1DataArrayWrite) {
      return L1D1cache.checkResourceAvailable(CacheResourceType:DataArray, addr);
    } else if (request_type == RequestType:L1D1TagArrayRead) {
      return L1D1cache.checkResourceAvailable(CacheResourceType:TagArray, addr);
    } else if (request_type == RequestType:L1D1TagArrayWrite) {
      return L1D1cache.checkResourceAvailable(CacheResourceType:TagArray, addr);
    } else if (request_type == RequestType:L1IDataArrayRead) {
      return L1Icache.checkResourceAvailable(CacheResourceType:DataArray, addr);
    } else if (request_type == RequestType:L1IDataArrayWrite) {
      return L1Icache.checkResourceAvailable(CacheResourceType:DataArray, addr);
    } else if (request_type == RequestType:L1ITagArrayRead) {
      return L1Icache.checkResourceAvailable(CacheResourceType:TagArray, addr);
    } else if (request_type == RequestType:L1ITagArrayWrite) {
      return L1Icache.checkResourceAvailable(CacheResourceType:TagArray, addr);
    } else {
      return true;
    }
  }

  // END INTERNAL FUNCTIONS

  // ** OUT_PORTS **

  out_port(requestNetwork_out, CPURequestMsg, requestFromCore);
  out_port(responseNetwork_out, ResponseMsg, responseFromCore);
  out_port(triggerQueue_out, TriggerMsg, triggerQueue);
  out_port(unblockNetwork_out, UnblockMsg, unblockFromCore);

  // ** IN_PORTS **

  in_port(triggerQueue_in, TriggerMsg, triggerQueue, block_on="addr") {
    if (triggerQueue_in.isReady(clockEdge())) {
      peek(triggerQueue_in, TriggerMsg) {
        Entry cache_entry := getCacheEntry(in_msg.addr);
        TBE tbe := TBEs.lookup(in_msg.addr);

        if (in_msg.Type == TriggerType:L2_to_L1) {
          if (in_msg.Dest == CacheId:L1I) {
            trigger(Event:L2_to_L1I, in_msg.addr, cache_entry, tbe);
          } else if (in_msg.Dest == CacheId:L1D0) {
            trigger(Event:L2_to_L1D0, in_msg.addr, cache_entry, tbe);
          } else if (in_msg.Dest == CacheId:L1D1) {
            trigger(Event:L2_to_L1D1, in_msg.addr, cache_entry, tbe);
          } else {
            error("unexpected trigger dest");
          }
        }
      }
    }
  }


  in_port(probeNetwork_in, NBProbeRequestMsg, probeToCore) {
    if (probeNetwork_in.isReady(clockEdge())) {
      peek(probeNetwork_in, NBProbeRequestMsg, block_on="addr") {
        Entry cache_entry := getCacheEntry(in_msg.addr);
        TBE tbe := TBEs.lookup(in_msg.addr);

        if (in_msg.Type == ProbeRequestType:PrbInv) {
          if (in_msg.DemandRequest) {
            trigger(Event:PrbInvDataDemand, in_msg.addr, cache_entry, tbe);
          } else if (in_msg.ReturnData) {
            trigger(Event:PrbInvData, in_msg.addr, cache_entry, tbe);
          } else {
            trigger(Event:PrbInv, in_msg.addr, cache_entry, tbe);
          }
        } else if (in_msg.Type == ProbeRequestType:PrbDowngrade) {
          if (in_msg.DemandRequest) {
               trigger(Event:PrbShrDataDemand, in_msg.addr, cache_entry, tbe);
          } else {
               assert(in_msg.ReturnData);
               trigger(Event:PrbShrData, in_msg.addr, cache_entry, tbe);
          }
        } else if (in_msg.Type == ProbeRequestType:PrbRepl) {
          trigger(Event:ForceRepl, in_msg.addr, cache_entry, tbe);
        } else if (in_msg.Type == ProbeRequestType:PrbRegDowngrade) {
          trigger(Event:ForceDowngrade, in_msg.addr, cache_entry, tbe);
        } else {
          error("Unknown probe request");
        }
      }
    }
  }


  // ResponseNetwork
  in_port(responseToCore_in, ResponseMsg, responseToCore) {
    if (responseToCore_in.isReady(clockEdge())) {
      peek(responseToCore_in, ResponseMsg, block_on="addr") {

        Entry cache_entry := getCacheEntry(in_msg.addr);
        TBE tbe := TBEs.lookup(in_msg.addr);

        if (in_msg.Type == CoherenceResponseType:NBSysResp) {
          if (in_msg.State == CoherenceState:Modified) {
              trigger(Event:NB_AckM, in_msg.addr, cache_entry, tbe);
          } else if (in_msg.State == CoherenceState:Shared) {
            trigger(Event:NB_AckS, in_msg.addr, cache_entry, tbe);
          } else if (in_msg.State == CoherenceState:Exclusive) {
            trigger(Event:NB_AckE, in_msg.addr, cache_entry, tbe);
          }
        } else if (in_msg.Type == CoherenceResponseType:NBSysWBAck) {
          trigger(Event:NB_AckWB, in_msg.addr, cache_entry, tbe);
        } else {
          error("Unexpected Response Message to Core");
        }
      }
    }
  }

  // Nothing from the Unblock Network

  // Mandatory Queue
  in_port(mandatoryQueue_in, RubyRequest, mandatoryQueue, desc="...") {
    if (mandatoryQueue_in.isReady(clockEdge())) {
      peek(mandatoryQueue_in, RubyRequest, block_on="LineAddress") {

        Entry cache_entry := getCacheEntry(in_msg.LineAddress);
        TBE tbe := TBEs.lookup(in_msg.LineAddress);

        if (in_msg.Type == RubyRequestType:IFETCH) {
          // FETCH ACCESS

          if (L1Icache.isTagPresent(in_msg.LineAddress)) {
            if (mod(in_msg.contextId, 2) == 0) {
              trigger(Event:Ifetch0_L1hit, in_msg.LineAddress, cache_entry, tbe);
            } else {
              trigger(Event:Ifetch1_L1hit, in_msg.LineAddress, cache_entry, tbe);
            }
          } else {
            if (presentOrAvail2(in_msg.LineAddress)) {
              if (presentOrAvailI(in_msg.LineAddress)) {
                if (mod(in_msg.contextId, 2) == 0) {
                  trigger(Event:Ifetch0_L1miss, in_msg.LineAddress, cache_entry,
                          tbe);
                } else {
                  trigger(Event:Ifetch1_L1miss, in_msg.LineAddress, cache_entry,
                          tbe);
                }
              } else {
                Addr victim := L1Icache.cacheProbe(in_msg.LineAddress);
                trigger(Event:L1I_Repl, victim,
                        getCacheEntry(victim), TBEs.lookup(victim));
              }
            } else { // Not present or avail in L2
              Addr victim := L2cache.cacheProbe(in_msg.LineAddress);
              DPRINTF(RubySlicc, "Victim for %s L2_Repl(0) is %s\n", in_msg.LineAddress, victim);
              trigger(Event:L2_Repl, victim, getCacheEntry(victim),
                      TBEs.lookup(victim));
            }
          }
        } else {
          // DATA ACCESS
          if (mod(in_msg.contextId, 2) == 1) {
            if (L1D1cache.isTagPresent(in_msg.LineAddress)) {
              if (in_msg.Type == RubyRequestType:LD) {
                trigger(Event:C1_Load_L1hit, in_msg.LineAddress, cache_entry,
                        tbe);
              } else {
                // Stores must write through, make sure L2 avail.
                if (presentOrAvail2(in_msg.LineAddress)) {
                  trigger(Event:C1_Store_L1hit, in_msg.LineAddress, cache_entry,
                          tbe);
                } else {
                  Addr victim := L2cache.cacheProbe(in_msg.LineAddress);
              DPRINTF(RubySlicc, "Victim for %s L2_Repl(1) is %s\n", in_msg.LineAddress, victim);
                  trigger(Event:L2_Repl, victim, getCacheEntry(victim),
                          TBEs.lookup(victim));
                }
              }
            } else {
              if (presentOrAvail2(in_msg.LineAddress)) {
                if (presentOrAvailD1(in_msg.LineAddress)) {
                  if (in_msg.Type == RubyRequestType:LD) {
                    trigger(Event:C1_Load_L1miss, in_msg.LineAddress,
                            cache_entry, tbe);
                  } else {
                    trigger(Event:C1_Store_L1miss, in_msg.LineAddress,
                            cache_entry, tbe);
                  }
                } else {
                  Addr victim := L1D1cache.cacheProbe(in_msg.LineAddress);
              DPRINTF(RubySlicc, "Victim for %s L1D1_Repl is %s\n", in_msg.LineAddress, victim);
                  trigger(Event:L1D1_Repl, victim,
                          getCacheEntry(victim), TBEs.lookup(victim));
                }
              } else { // not present or avail in L2
                Addr victim := L2cache.cacheProbe(in_msg.LineAddress);
              DPRINTF(RubySlicc, "Victim for %s L2_Repl(2) is %s\n", in_msg.LineAddress, victim);
                trigger(Event:L2_Repl, victim, getCacheEntry(victim), TBEs.lookup(victim));
              }
            }
          } else {
            Entry L1D0cache_entry := getL1CacheEntry(in_msg.LineAddress, 0);
            if (is_valid(L1D0cache_entry)) {
              if (in_msg.Type == RubyRequestType:LD) {
                trigger(Event:C0_Load_L1hit, in_msg.LineAddress, cache_entry,
                    tbe);
              } else {
                if (presentOrAvail2(in_msg.LineAddress)) {
                  trigger(Event:C0_Store_L1hit, in_msg.LineAddress, cache_entry,
                      tbe);
                } else {
                  Addr victim := L2cache.cacheProbe(in_msg.LineAddress);
              DPRINTF(RubySlicc, "Victim for %s L2_Repl(3) is %s\n", in_msg.LineAddress, victim);
                  trigger(Event:L2_Repl, victim, getCacheEntry(victim),
                      TBEs.lookup(victim));
                }
              }
            } else {
              if (presentOrAvail2(in_msg.LineAddress)) {
                if (presentOrAvailD0(in_msg.LineAddress)) {
                  if (in_msg.Type == RubyRequestType:LD) {
                    trigger(Event:C0_Load_L1miss, in_msg.LineAddress,
                        cache_entry, tbe);
                  } else {
                    trigger(Event:C0_Store_L1miss, in_msg.LineAddress,
                            cache_entry, tbe);
                  }
                } else {
                  Addr victim := L1D0cache.cacheProbe(in_msg.LineAddress);
              DPRINTF(RubySlicc, "Victim for %s L1D0_Repl is %s\n", in_msg.LineAddress, victim);
                  trigger(Event:L1D0_Repl, victim, getCacheEntry(victim),
                          TBEs.lookup(victim));
                }
              } else {
                Addr victim := L2cache.cacheProbe(in_msg.LineAddress);
              DPRINTF(RubySlicc, "Victim for %s L2_Repl(4) is %s\n", in_msg.LineAddress, victim);
                trigger(Event:L2_Repl, victim, getCacheEntry(victim),
                        TBEs.lookup(victim));
              }
            }
          }
        }
      }
    }
  }


  // ACTIONS
  action(ii_invIcache, "ii", desc="invalidate iCache") {
    if (L1Icache.isTagPresent(address)) {
      L1Icache.deallocate(address);
    }
  }

  action(i0_invCluster, "i0", desc="invalidate cluster 0") {
    if (L1D0cache.isTagPresent(address)) {
      L1D0cache.deallocate(address);
    }
  }

  action(i1_invCluster, "i1", desc="invalidate cluster 1") {
    if (L1D1cache.isTagPresent(address)) {
      L1D1cache.deallocate(address);
    }
  }

  action(ib_invBothClusters, "ib", desc="invalidate both clusters") {
    if (L1D0cache.isTagPresent(address)) {
      L1D0cache.deallocate(address);
    }
    if (L1D1cache.isTagPresent(address)) {
      L1D1cache.deallocate(address);
    }
  }

  action(i2_invL2, "i2", desc="invalidate L2") {
    if(is_valid(cache_entry)) {
        L2cache.deallocate(address);
    }
    unset_cache_entry();
  }

  action(n_issueRdBlk, "n", desc="Issue RdBlk") {
    enqueue(requestNetwork_out, CPURequestMsg, issue_latency) {
      out_msg.addr := address;
      out_msg.Type := CoherenceRequestType:RdBlk;
      out_msg.Requestor := machineID;
      out_msg.Destination.add(getPeer(machineID));
      out_msg.MessageSize := MessageSizeType:Request_Control;
      out_msg.InitialRequestTime := curCycle();
    }
  }

  action(nM_issueRdBlkM, "nM", desc="Issue RdBlkM") {
    enqueue(requestNetwork_out, CPURequestMsg, issue_latency) {
      out_msg.addr := address;
      out_msg.Type := CoherenceRequestType:RdBlkM;
      out_msg.Requestor := machineID;
      out_msg.Destination.add(getPeer(machineID));
      out_msg.MessageSize := MessageSizeType:Request_Control;
      out_msg.InitialRequestTime := curCycle();
    }
  }

  action(nMs_issueRdBlkMSinked, "nMs", desc="Issue RdBlkM with CtoDSinked") {
    enqueue(requestNetwork_out, CPURequestMsg, issue_latency) {
      out_msg.addr := address;
      out_msg.Type := CoherenceRequestType:RdBlkM;
      out_msg.Requestor := machineID;
      out_msg.Destination.add(getPeer(machineID));
      out_msg.MessageSize := MessageSizeType:Request_Control;
      out_msg.CtoDSinked := true;
    }
  }

  action(nS_issueRdBlkS, "nS", desc="Issue RdBlkS") {
    enqueue(requestNetwork_out, CPURequestMsg, issue_latency) {
      out_msg.addr := address;
      out_msg.Type := CoherenceRequestType:RdBlkS;
      out_msg.Requestor := machineID;
      out_msg.Destination.add(getPeer(machineID));
      out_msg.MessageSize := MessageSizeType:Request_Control;
      out_msg.InitialRequestTime := curCycle();
    }
  }

  action(nSs_issueRdBlkSSinked, "nSs", desc="Issue RdBlkS with CtoDSinked") {
    enqueue(requestNetwork_out, CPURequestMsg, issue_latency) {
      out_msg.addr := address;
      out_msg.Type := CoherenceRequestType:RdBlkS;
      out_msg.Requestor := machineID;
      out_msg.Destination.add(getPeer(machineID));
      out_msg.CtoDSinked := true;
      out_msg.MessageSize := MessageSizeType:Request_Control;
    }
  }

  action(vd_victim, "vd", desc="Victimize M/O L2 Data") {
    enqueue(requestNetwork_out, CPURequestMsg, issue_latency) {
      out_msg.addr := address;
      out_msg.Requestor := machineID;
      assert(is_valid(cache_entry));
      out_msg.DataBlk := cache_entry.DataBlk;
      assert(cache_entry.Dirty);
      out_msg.Destination.add(getPeer(machineID));
      out_msg.MessageSize := MessageSizeType:Request_Control;
      out_msg.Type := CoherenceRequestType:VicDirty;
      out_msg.InitialRequestTime := curCycle();
      if (cache_entry.CacheState == State:O) {
        out_msg.Shared := true;
      } else {
        out_msg.Shared := false;
      }
    }
  }

  action(vc_victim, "vc", desc="Victimize E/S L2 Data") {
    enqueue(requestNetwork_out, CPURequestMsg, issue_latency) {
      out_msg.addr := address;
      out_msg.Requestor := machineID;
      out_msg.Destination.add(getPeer(machineID));
      out_msg.MessageSize := MessageSizeType:Request_Control;
      out_msg.Type := CoherenceRequestType:VicClean;
      out_msg.InitialRequestTime := curCycle();
      if (cache_entry.CacheState == State:S) {
        out_msg.Shared := true;
      } else {
        out_msg.Shared := false;
      }
    }
  }

  // Could send these two directly to dir if we made a new out network on channel 0
  action(vdf_victimForce, "vdf", desc="Victimize M/O L2 Data") {
    enqueue(requestNetwork_out, CPURequestMsg, issue_latency) {
      out_msg.addr := address;
      out_msg.Requestor := machineID;
      assert(is_valid(cache_entry));
      out_msg.DataBlk := cache_entry.DataBlk;
      assert(cache_entry.Dirty);
      out_msg.Destination.add(getPeer(machineID));
      out_msg.MessageSize := MessageSizeType:Request_Control;
      out_msg.Type := CoherenceRequestType:VicDirty;
      out_msg.InitialRequestTime := curCycle();
      if (cache_entry.CacheState == State:O) {
        out_msg.Shared := true;
      } else {
        out_msg.Shared := false;
      }
      out_msg.Private := true;
    }
  }

  action(vcf_victimForce, "vcf", desc="Victimize E/S L2 Data") {
    enqueue(requestNetwork_out, CPURequestMsg, issue_latency) {
      out_msg.addr := address;
      out_msg.Requestor := machineID;
      out_msg.Destination.add(getPeer(machineID));
      out_msg.MessageSize := MessageSizeType:Request_Control;
      out_msg.Type := CoherenceRequestType:VicClean;
      out_msg.InitialRequestTime := curCycle();
      if (cache_entry.CacheState == State:S) {
        out_msg.Shared := true;
      } else {
        out_msg.Shared := false;
      }
      out_msg.Private := true;
    }
  }

  action(a0_allocateL1D, "a0", desc="Allocate L1D0 Block") {
    if (L1D0cache.isTagPresent(address) == false) {
      L1D0cache.allocateVoid(address, new Entry);
    }
  }

  action(a1_allocateL1D, "a1", desc="Allocate L1D1 Block") {
    if (L1D1cache.isTagPresent(address) == false) {
      L1D1cache.allocateVoid(address, new Entry);
    }
  }

  action(ai_allocateL1I, "ai", desc="Allocate L1I Block") {
    if (L1Icache.isTagPresent(address) == false) {
      L1Icache.allocateVoid(address, new Entry);
    }
  }

  action(a2_allocateL2, "a2", desc="Allocate L2 Block") {
    if (is_invalid(cache_entry)) {
      set_cache_entry(L2cache.allocate(address, new Entry));
    }
  }

  action(t_allocateTBE, "t", desc="allocate TBE Entry") {
    check_allocate(TBEs);
    assert(is_valid(cache_entry));
    TBEs.allocate(address);
    set_tbe(TBEs.lookup(address));
    tbe.DataBlk := cache_entry.DataBlk;  // Data only used for WBs
    tbe.Dirty := cache_entry.Dirty;
    tbe.Shared := false;
  }

  action(d_deallocateTBE, "d", desc="Deallocate TBE") {
    TBEs.deallocate(address);
    unset_tbe();
  }

  action(p_popMandatoryQueue, "pm", desc="Pop Mandatory Queue") {
    mandatoryQueue_in.dequeue(clockEdge());
  }

  action(pr_popResponseQueue, "pr", desc="Pop Response Queue") {
    responseToCore_in.dequeue(clockEdge());
  }

  action(pt_popTriggerQueue, "pt", desc="Pop Trigger Queue") {
    triggerQueue_in.dequeue(clockEdge());
  }

  action(pp_popProbeQueue, "pp", desc="pop probe queue") {
    probeNetwork_in.dequeue(clockEdge());
  }

  action(il0_loadDone, "il0", desc="Cluster 0 i load done") {
    Entry entry := getICacheEntry(address);
    Entry l2entry := getCacheEntry(address); // Used for functional accesses
    assert(is_valid(entry));
    // L2 supplies data (functional accesses only look in L2, ok because L1
    //                   writes through to L2)
    sequencer.readCallback(address,
                           l2entry.DataBlk,
                           true,
                           testAndClearLocalHit(entry));
  }

  action(il1_loadDone, "il1", desc="Cluster 1 i load done") {
    Entry entry := getICacheEntry(address);
    Entry l2entry := getCacheEntry(address); // Used for functional accesses
    assert(is_valid(entry));
    // L2 supplies data (functional accesses only look in L2, ok because L1
    //                   writes through to L2)
    sequencer1.readCallback(address,
                            l2entry.DataBlk,
                            true,
                            testAndClearLocalHit(entry));
  }

  action(l0_loadDone, "l0", desc="Cluster 0 load done") {
    Entry entry := getL1CacheEntry(address, 0);
    Entry l2entry := getCacheEntry(address); // Used for functional accesses
    assert(is_valid(entry));
    // L2 supplies data (functional accesses only look in L2, ok because L1
    //                   writes through to L2)
    sequencer.readCallback(address,
                           l2entry.DataBlk,
                           true,
                           testAndClearLocalHit(entry));
  }

  action(l1_loadDone, "l1", desc="Cluster 1 load done") {
    Entry entry := getL1CacheEntry(address, 1);
    Entry l2entry := getCacheEntry(address); // Used for functional accesses
    assert(is_valid(entry));
    // L2 supplies data (functional accesses only look in L2, ok because L1
    //                   writes through to L2)
    sequencer1.readCallback(address,
                            l2entry.DataBlk,
                            true,
                            testAndClearLocalHit(entry));
  }

  action(xl0_loadDone, "xl0", desc="Cluster 0 load done") {
    peek(responseToCore_in, ResponseMsg) {
      assert((machineIDToMachineType(in_msg.Sender) == MachineType:Directory) ||
              (machineIDToMachineType(in_msg.Sender) == MachineType:L3Cache));
      Entry l2entry := getCacheEntry(address); // Used for functional accesses
      DPRINTF(ProtocolTrace, "CP Load Done 0 -- address %s, data: %s\n",
              address, l2entry.DataBlk);
      // L2 supplies data (functional accesses only look in L2, ok because L1
      //                   writes through to L2)
      assert(is_valid(l2entry));
      sequencer.readCallback(address,
                             l2entry.DataBlk,
                             false,
                             machineIDToMachineType(in_msg.Sender),
                             in_msg.InitialRequestTime,
                             in_msg.ForwardRequestTime,
                             in_msg.ProbeRequestStartTime);
    }
  }

  action(xl1_loadDone, "xl1", desc="Cluster 1 load done") {
   peek(responseToCore_in, ResponseMsg) {
      assert((machineIDToMachineType(in_msg.Sender) == MachineType:Directory) ||
              (machineIDToMachineType(in_msg.Sender) == MachineType:L3Cache));
      Entry l2entry := getCacheEntry(address); // Used for functional accesses
      // L2 supplies data (functional accesses only look in L2, ok because L1
      //                   writes through to L2)
      assert(is_valid(l2entry));
      sequencer1.readCallback(address,
                              l2entry.DataBlk,
                              false,
                              machineIDToMachineType(in_msg.Sender),
                              in_msg.InitialRequestTime,
                              in_msg.ForwardRequestTime,
                              in_msg.ProbeRequestStartTime);
   }
  }

  action(xi0_loadDone, "xi0", desc="Cluster 0 i-load done") {
    peek(responseToCore_in, ResponseMsg) {
      assert((machineIDToMachineType(in_msg.Sender) == MachineType:Directory) ||
              (machineIDToMachineType(in_msg.Sender) == MachineType:L3Cache));
      Entry l2entry := getCacheEntry(address); // Used for functional accesses
      // L2 supplies data (functional accesses only look in L2, ok because L1
      //                   writes through to L2)
      assert(is_valid(l2entry));
      sequencer.readCallback(address,
                             l2entry.DataBlk,
                             false,
                             machineIDToMachineType(in_msg.Sender),
                             in_msg.InitialRequestTime,
                             in_msg.ForwardRequestTime,
                             in_msg.ProbeRequestStartTime);
    }
  }

  action(xi1_loadDone, "xi1", desc="Cluster 1 i-load done") {
    peek(responseToCore_in, ResponseMsg) {
      assert((machineIDToMachineType(in_msg.Sender) == MachineType:Directory) ||
              (machineIDToMachineType(in_msg.Sender) == MachineType:L3Cache));
      Entry l2entry := getCacheEntry(address); // Used for functional accesses
      // L2 supplies data (functional accesses only look in L2, ok because L1
      //                   writes through to L2)
      assert(is_valid(l2entry));
      sequencer1.readCallback(address,
                              l2entry.DataBlk,
                              false,
                              machineIDToMachineType(in_msg.Sender),
                              in_msg.InitialRequestTime,
                              in_msg.ForwardRequestTime,
                              in_msg.ProbeRequestStartTime);
    }
  }

  action(s0_storeDone, "s0", desc="Cluster 0 store done") {
    Entry entry := getL1CacheEntry(address, 0);
    assert(is_valid(entry));
    assert(is_valid(cache_entry));
    sequencer.writeCallback(address,
                            cache_entry.DataBlk,
                            true,
                            testAndClearLocalHit(entry));
    cache_entry.Dirty := true;
    entry.DataBlk := cache_entry.DataBlk;
    entry.Dirty := true;
    DPRINTF(RubySlicc, "%s\n", cache_entry.DataBlk);
  }

  action(s1_storeDone, "s1", desc="Cluster 1 store done") {
    Entry entry := getL1CacheEntry(address, 1);
    assert(is_valid(entry));
    assert(is_valid(cache_entry));
    sequencer1.writeCallback(address,
                             cache_entry.DataBlk,
                             true,
                             testAndClearLocalHit(entry));
    cache_entry.Dirty := true;
    entry.Dirty := true;
    entry.DataBlk := cache_entry.DataBlk;
    DPRINTF(RubySlicc, "%s\n", cache_entry.DataBlk);
  }

  action(xs0_storeDone, "xs0", desc="Cluster 0 store done") {
    peek(responseToCore_in, ResponseMsg) {
      Entry entry := getL1CacheEntry(address, 0);
      assert(is_valid(entry));
      assert(is_valid(cache_entry));
      assert((machineIDToMachineType(in_msg.Sender) == MachineType:Directory) ||
             (machineIDToMachineType(in_msg.Sender) == MachineType:L3Cache));
      sequencer.writeCallback(address,
                              cache_entry.DataBlk,
                              false,
                              machineIDToMachineType(in_msg.Sender),
                              in_msg.InitialRequestTime,
                              in_msg.ForwardRequestTime,
                              in_msg.ProbeRequestStartTime);
      cache_entry.Dirty := true;
      entry.Dirty := true;
      entry.DataBlk := cache_entry.DataBlk;
      DPRINTF(RubySlicc, "%s\n", cache_entry.DataBlk);
    }
  }

  action(xs1_storeDone, "xs1", desc="Cluster 1 store done") {
    peek(responseToCore_in, ResponseMsg) {
      Entry entry := getL1CacheEntry(address, 1);
      assert(is_valid(entry));
      assert(is_valid(cache_entry));
      assert((machineIDToMachineType(in_msg.Sender) == MachineType:Directory) ||
             (machineIDToMachineType(in_msg.Sender) == MachineType:L3Cache));
      sequencer1.writeCallback(address,
                               cache_entry.DataBlk,
                               false,
                               machineIDToMachineType(in_msg.Sender),
                               in_msg.InitialRequestTime,
                               in_msg.ForwardRequestTime,
                               in_msg.ProbeRequestStartTime);
      cache_entry.Dirty := true;
      entry.Dirty := true;
      entry.DataBlk := cache_entry.DataBlk;
      DPRINTF(RubySlicc, "%s\n", cache_entry.DataBlk);
    }
  }

  action(forward_eviction_to_cpu0, "fec0", desc="sends eviction information to processor0") {
    if (send_evictions) {
      DPRINTF(RubySlicc, "Sending invalidation for %s to the CPU\n", address);
      sequencer.evictionCallback(address);
    }
  }

  action(forward_eviction_to_cpu1, "fec1", desc="sends eviction information to processor1") {
    if (send_evictions) {
      DPRINTF(RubySlicc, "Sending invalidation for %s to the CPU\n", address);
      sequencer1.evictionCallback(address);
    }
  }

  action(ci_copyL2ToL1, "ci", desc="copy L2 data to L1") {
    Entry entry := getICacheEntry(address);
    assert(is_valid(entry));
    assert(is_valid(cache_entry));
    entry.Dirty := cache_entry.Dirty;
    entry.DataBlk := cache_entry.DataBlk;
    entry.FromL2 := true;
  }

  action(c0_copyL2ToL1, "c0", desc="copy L2 data to L1") {
    Entry entry := getL1CacheEntry(address, 0);
    assert(is_valid(entry));
    assert(is_valid(cache_entry));
    entry.Dirty := cache_entry.Dirty;
    entry.DataBlk := cache_entry.DataBlk;
    entry.FromL2 := true;
  }

  action(ss_sendStaleNotification, "ss", desc="stale data; nothing to writeback") {
    peek(responseToCore_in, ResponseMsg) {
      enqueue(responseNetwork_out, ResponseMsg, issue_latency) {
        out_msg.addr := address;
        out_msg.Type := CoherenceResponseType:StaleNotif;
        out_msg.Sender := machineID;
        out_msg.Destination.add(map_Address_to_Directory(address));
        out_msg.MessageSize := MessageSizeType:Response_Control;
        DPRINTF(RubySlicc, "%s\n", out_msg);
      }
    }
  }

  action(c1_copyL2ToL1, "c1", desc="copy L2 data to L1") {
    Entry entry := getL1CacheEntry(address, 1);
    assert(is_valid(entry));
    assert(is_valid(cache_entry));
    entry.Dirty := cache_entry.Dirty;
    entry.DataBlk := cache_entry.DataBlk;
    entry.FromL2 := true;
  }

  action(fi_L2ToL1, "fi", desc="L2 to L1 inst fill") {
    enqueue(triggerQueue_out, TriggerMsg, l2_hit_latency) {
      out_msg.addr := address;
      out_msg.Type := TriggerType:L2_to_L1;
      out_msg.Dest := CacheId:L1I;
    }
  }

  action(f0_L2ToL1, "f0", desc="L2 to L1 data fill") {
    enqueue(triggerQueue_out, TriggerMsg, l2_hit_latency) {
      out_msg.addr := address;
      out_msg.Type := TriggerType:L2_to_L1;
      out_msg.Dest := CacheId:L1D0;
    }
  }

  action(f1_L2ToL1, "f1", desc="L2 to L1 data fill") {
    enqueue(triggerQueue_out, TriggerMsg, l2_hit_latency) {
      out_msg.addr := address;
      out_msg.Type := TriggerType:L2_to_L1;
      out_msg.Dest := CacheId:L1D1;
    }
  }

  action(wi_writeIcache, "wi", desc="write data to icache (and l2)") {
    peek(responseToCore_in, ResponseMsg) {
      Entry entry := getICacheEntry(address);
      assert(is_valid(entry));
      assert(is_valid(cache_entry));
      entry.DataBlk := in_msg.DataBlk;
      entry.Dirty := in_msg.Dirty;
      cache_entry.DataBlk := in_msg.DataBlk;
      cache_entry.Dirty := in_msg.Dirty;
    }
  }

  action(w0_writeDcache, "w0", desc="write data to dcache 0 (and l2)") {
    peek(responseToCore_in, ResponseMsg) {
      Entry entry := getL1CacheEntry(address, 0);
      assert(is_valid(entry));
      assert(is_valid(cache_entry));
      entry.DataBlk := in_msg.DataBlk;
      entry.Dirty := in_msg.Dirty;
      cache_entry.DataBlk := in_msg.DataBlk;
      cache_entry.Dirty := in_msg.Dirty;
    }
  }

  action(w1_writeDcache, "w1", desc="write data to dcache 1 (and l2)") {
    peek(responseToCore_in, ResponseMsg) {
      Entry entry := getL1CacheEntry(address, 1);
      assert(is_valid(entry));
      assert(is_valid(cache_entry));
      entry.DataBlk := in_msg.DataBlk;
      entry.Dirty := in_msg.Dirty;
      cache_entry.DataBlk := in_msg.DataBlk;
      cache_entry.Dirty := in_msg.Dirty;
    }
  }

  action(wb_data, "wb", desc="write back data") {
    peek(responseToCore_in, ResponseMsg) {
      enqueue(responseNetwork_out, ResponseMsg, issue_latency) {
        out_msg.addr := address;
        out_msg.Type := CoherenceResponseType:CPUData;
        out_msg.Sender := machineID;
        out_msg.Destination.add(map_Address_to_Directory(address));
        out_msg.DataBlk := tbe.DataBlk;
        out_msg.Dirty := tbe.Dirty;
        if (tbe.Shared) {
          out_msg.NbReqShared := true;
        } else {
          out_msg.NbReqShared := false;
        }
        out_msg.State := CoherenceState:Shared; // faux info
        out_msg.MessageSize := MessageSizeType:Writeback_Data;
        DPRINTF(RubySlicc, "%s\n", out_msg);
      }
    }
  }

  action(pi_sendProbeResponseInv, "pi", desc="send probe ack inv, no data") {
    enqueue(responseNetwork_out, ResponseMsg, issue_latency) {
      out_msg.addr := address;
      out_msg.Type := CoherenceResponseType:CPUPrbResp;  // L3 and CPUs respond in same way to probes
      out_msg.Sender := machineID;
      out_msg.Destination.add(map_Address_to_Directory(address)); // will this always be ok? probably not for multisocket
      out_msg.Dirty := false;
      out_msg.Hit := false;
      out_msg.Ntsl := true;
      out_msg.State := CoherenceState:NA;
      out_msg.MessageSize := MessageSizeType:Response_Control;
      out_msg.isValid := isValid(address);
    }
  }

  action(pim_sendProbeResponseInvMs, "pim", desc="send probe ack inv, no data") {
    enqueue(responseNetwork_out, ResponseMsg, issue_latency) {
      out_msg.addr := address;
      out_msg.Type := CoherenceResponseType:CPUPrbResp;  // L3 and CPUs respond in same way to probes
      out_msg.Sender := machineID;
      out_msg.Destination.add(map_Address_to_Directory(address)); // will this always be ok? probably not for multisocket
      out_msg.Dirty := false;
      out_msg.Ntsl := true;
      out_msg.Hit := false;
      APPEND_TRANSITION_COMMENT("Setting Ms");
      out_msg.State := CoherenceState:NA;
      out_msg.MessageSize := MessageSizeType:Response_Control;
      out_msg.isValid := isValid(address);
    }
  }

  action(ph_sendProbeResponseHit, "ph", desc="send probe ack PrbShrData, no data") {
    enqueue(responseNetwork_out, ResponseMsg, issue_latency) {
      out_msg.addr := address;
      out_msg.Type := CoherenceResponseType:CPUPrbResp;  // L3 and CPUs respond in same way to probes
      out_msg.Sender := machineID;
      out_msg.Destination.add(map_Address_to_Directory(address)); // will this always be ok? probably not for multisocket
      assert(addressInCore(address) || is_valid(tbe));
      out_msg.Dirty := false;  // only true if sending back data i think
      out_msg.Hit := true;
      out_msg.Ntsl := false;
      out_msg.State := CoherenceState:NA;
      out_msg.MessageSize := MessageSizeType:Response_Control;
      out_msg.isValid := isValid(address);
    }
  }

  action(pb_sendProbeResponseBackprobe, "pb", desc="send probe ack PrbShrData, no data, check for L1 residence") {
    enqueue(responseNetwork_out, ResponseMsg, issue_latency) {
      out_msg.addr := address;
      out_msg.Type := CoherenceResponseType:CPUPrbResp;  // L3 and CPUs respond in same way to probes
      out_msg.Sender := machineID;
      out_msg.Destination.add(map_Address_to_Directory(address)); // will this always be ok? probably not for multisocket
      if (addressInCore(address)) {
        out_msg.Hit := true;
      } else {
        out_msg.Hit := false;
      }
      out_msg.Dirty := false;  // not sending back data, so def. not dirty
      out_msg.Ntsl := false;
      out_msg.State := CoherenceState:NA;
      out_msg.MessageSize := MessageSizeType:Response_Control;
      out_msg.isValid := isValid(address);
    }
  }

  action(pd_sendProbeResponseData, "pd", desc="send probe ack, with data") {
    enqueue(responseNetwork_out, ResponseMsg, issue_latency) {
      assert(is_valid(cache_entry));
      out_msg.addr := address;
      out_msg.Type := CoherenceResponseType:CPUPrbResp;
      out_msg.Sender := machineID;
      out_msg.Destination.add(map_Address_to_Directory(address)); // will this always be ok? probably not for multisocket
      out_msg.DataBlk := cache_entry.DataBlk;
      assert(cache_entry.Dirty);
      out_msg.Dirty := true;
      out_msg.Hit := true;
      out_msg.State := CoherenceState:NA;
      out_msg.MessageSize := MessageSizeType:Response_Data;
      out_msg.isValid := isValid(address);
    }
  }

  action(pdm_sendProbeResponseDataMs, "pdm", desc="send probe ack, with data") {
    enqueue(responseNetwork_out, ResponseMsg, issue_latency) {
      assert(is_valid(cache_entry));
      out_msg.addr := address;
      out_msg.Type := CoherenceResponseType:CPUPrbResp;
      out_msg.Sender := machineID;
      out_msg.Destination.add(map_Address_to_Directory(address)); // will this always be ok? probably not for multisocket
      out_msg.DataBlk := cache_entry.DataBlk;
      assert(cache_entry.Dirty);
      out_msg.Dirty := true;
      out_msg.Hit := true;
      APPEND_TRANSITION_COMMENT("Setting Ms");
      out_msg.State := CoherenceState:NA;
      out_msg.MessageSize := MessageSizeType:Response_Data;
      out_msg.isValid := isValid(address);
    }
  }

  action(pdt_sendProbeResponseDataFromTBE, "pdt", desc="send probe ack with data") {
    enqueue(responseNetwork_out, ResponseMsg, issue_latency) {
      assert(is_valid(tbe));
      out_msg.addr := address;
      out_msg.Type := CoherenceResponseType:CPUPrbResp;
      out_msg.Sender := machineID;
      out_msg.Destination.add(map_Address_to_Directory(address));
      out_msg.DataBlk := tbe.DataBlk;
      assert(tbe.Dirty);
      out_msg.Dirty := true;
      out_msg.Hit := true;
      out_msg.State := CoherenceState:NA;
      out_msg.MessageSize := MessageSizeType:Response_Data;
      out_msg.isValid := isValid(address);
    }
  }

  action(ra_sendReplAck, "ra", desc="Send ack to r-buf that line is replaced if needed") {
    if (is_invalid(tbe) || tbe.AckNeeded) {
      enqueue(requestNetwork_out, CPURequestMsg, issue_latency) {
        out_msg.addr := address;
        out_msg.Type := CoherenceRequestType:InvAck;
        out_msg.Requestor := machineID;
        out_msg.Destination.add(getPeer(machineID));
        out_msg.MessageSize := MessageSizeType:Request_Control;
      }
      APPEND_TRANSITION_COMMENT(" Sending ack to r-buf ");
    } else {
      APPEND_TRANSITION_COMMENT(" NOT Sending ack to r-buf ");
    }
  }

  action(m_markAckNeeded, "m", desc="Mark TBE to send ack when deallocated") {
    assert(is_valid(tbe));
    tbe.AckNeeded := true;
  }

  action(mc_cancelWB, "mc", desc="send writeback cancel to L3") {
    enqueue(responseNetwork_out, ResponseMsg, issue_latency) {
      out_msg.addr := address;
      out_msg.Type := CoherenceResponseType:CPUCancelWB;
      out_msg.Destination.add(map_Address_to_Directory(address));
      out_msg.Sender := machineID;
      out_msg.MessageSize := MessageSizeType:Response_Control;
    }
  }

  action(s_setSharedFlip, "s", desc="hit by shared probe, status may be different") {
    assert(is_valid(tbe));
    tbe.Shared := true;
  }

  action(uu_sendUnblock, "uu", desc="state changed, unblock") {
    enqueue(unblockNetwork_out, UnblockMsg, issue_latency) {
      out_msg.addr := address;
      out_msg.Destination.add(map_Address_to_Directory(address));
      out_msg.MessageSize := MessageSizeType:Unblock_Control;
      out_msg.wasValid := isValid(address);
      DPRINTF(RubySlicc, "%s\n", out_msg);
    }
  }

  action(sdv_sendDoneValid, "sdv", desc="Request finished, send done ack") {
    enqueue(unblockNetwork_out, UnblockMsg, 1) {
      out_msg.addr := address;
      out_msg.Destination.add(getPeer(machineID));
      out_msg.DoneAck := true;
      out_msg.MessageSize := MessageSizeType:Unblock_Control;
      if (is_valid(tbe)) {
          out_msg.Dirty := tbe.Dirty;
      } else if (is_valid(cache_entry)) {
          out_msg.Dirty := cache_entry.Dirty;
      } else {
          out_msg.Dirty := false;
      }
      out_msg.validToInvalid := false;
      DPRINTF(RubySlicc, "%s\n", out_msg);
    }
  }

  action(sdi_sendDoneInvalid, "sdi", desc="Request finished, send done ack") {
    enqueue(unblockNetwork_out, UnblockMsg, 1) {
      out_msg.addr := address;
      out_msg.Destination.add(getPeer(machineID));
      out_msg.DoneAck := true;
      out_msg.MessageSize := MessageSizeType:Unblock_Control;
      if (is_valid(tbe)) {
          out_msg.Dirty := tbe.Dirty;
      } else if (is_valid(cache_entry)) {
          out_msg.Dirty := cache_entry.Dirty;
      } else {
          out_msg.Dirty := false;
      }
      out_msg.validToInvalid := true;
      DPRINTF(RubySlicc, "%s\n", out_msg);
    }
  }

  action(l10m_profileMiss, "l10m", desc="l10m miss profile") {
    ++L1D0cache.demand_misses;
  }

  action(l11m_profileMiss, "l11m", desc="l11m miss profile") {
    ++L1D1cache.demand_misses;
  }

  action(l1im_profileMiss, "l1lm", desc="l1im miss profile") {
    ++L1Icache.demand_misses;
  }

  action(l2m_profileMiss, "l2m", desc="l2m miss profile") {
    ++L2cache.demand_misses;
  }

  action(yy_recycleProbeQueue, "yy", desc="recycle probe queue") {
    probeNetwork_in.recycle(clockEdge(), cyclesToTicks(recycle_latency));
  }

  action(zz_recycleMandatoryQueue, "\z", desc="recycle mandatory queue") {
    mandatoryQueue_in.recycle(clockEdge(), cyclesToTicks(recycle_latency));
  }
  // END ACTIONS

  // BEGIN TRANSITIONS

  // transitions from base
  transition(I, C0_Load_L1miss, I_E0S) {L1D0TagArrayRead, L2TagArrayRead} {
    // track misses, if implemented
    // since in I state, L2 miss as well
    l2m_profileMiss;
    l10m_profileMiss;
    a0_allocateL1D;
    l1im_profileMiss;
    a2_allocateL2;
    i1_invCluster;
    ii_invIcache;
    n_issueRdBlk;
    p_popMandatoryQueue;
  }

  transition(I, C1_Load_L1miss, I_E1S) {L1D1TagArrayRead, L2TagArrayRead} {
    // track misses, if implemented
    // since in I state, L2 miss as well
    l2m_profileMiss;
    l11m_profileMiss;
    a1_allocateL1D;
    a2_allocateL2;
    i0_invCluster;
    ii_invIcache;
    n_issueRdBlk;
    p_popMandatoryQueue;
  }

  transition(I, Ifetch0_L1miss, S0) {L1ITagArrayRead, L2TagArrayRead} {
    // track misses, if implemented
    // L2 miss as well
    l10m_profileMiss;
    l2m_profileMiss;
    l1im_profileMiss;
    ai_allocateL1I;
    a2_allocateL2;
    ib_invBothClusters;
    nS_issueRdBlkS;
    p_popMandatoryQueue;
  }

  transition(I, Ifetch1_L1miss, S1) {L1ITagArrayRead, L2TagArrayRead} {
     l11m_profileMiss;
    // track misses, if implemented
    // L2 miss as well
    l2m_profileMiss;
    l1im_profileMiss;
    ai_allocateL1I;
    a2_allocateL2;
    ib_invBothClusters;
    nS_issueRdBlkS;
    p_popMandatoryQueue;
  }

  transition(I, C0_Store_L1miss, I_M0) {L1D0TagArrayRead,L2TagArrayRead} {
    l2m_profileMiss;
    l10m_profileMiss;
    a0_allocateL1D;
    a2_allocateL2;
    i1_invCluster;
    ii_invIcache;
    nM_issueRdBlkM;
    p_popMandatoryQueue;
  }

  transition(I, C1_Store_L1miss, I_M1) {L1D0TagArrayRead, L2TagArrayRead} {
    l2m_profileMiss;
    l11m_profileMiss;
    a1_allocateL1D;
    a2_allocateL2;
    i0_invCluster;
    ii_invIcache;
    nM_issueRdBlkM;
    p_popMandatoryQueue;
  }

  transition(S, C0_Load_L1miss, S_F0) {L1D0TagArrayRead, L2TagArrayRead, L2DataArrayRead} {
    l10m_profileMiss;
    a0_allocateL1D;
    f0_L2ToL1;
    p_popMandatoryQueue;
  }

  transition(S, C1_Load_L1miss, S_F1) {L1D1TagArrayRead,  L2TagArrayRead, L2DataArrayRead} {
    l11m_profileMiss;
    a1_allocateL1D;
    f1_L2ToL1;
    p_popMandatoryQueue;
  }

  transition(S, Ifetch0_L1miss, Si_F0) {L1ITagArrayRead,L2TagArrayRead, L2DataArrayRead} {
    l1im_profileMiss;
    ai_allocateL1I;
    fi_L2ToL1;
    p_popMandatoryQueue;
  }

  transition(S, Ifetch1_L1miss, Si_F1) {L1ITagArrayRead, L2TagArrayRead, L2DataArrayRead} {
    l1im_profileMiss;
    ai_allocateL1I;
    fi_L2ToL1;
    p_popMandatoryQueue;
  }

  transition({S}, {C0_Store_L1hit, C0_Store_L1miss}, S_M0) {L1D0TagArrayRead, L2TagArrayRead}{
    l2m_profileMiss;
    l10m_profileMiss;
    a0_allocateL1D;
    i1_invCluster;
    ii_invIcache;
    nM_issueRdBlkM;
    p_popMandatoryQueue;
  }

  transition({S}, {C1_Store_L1hit, C1_Store_L1miss}, S_M1) {L1D1TagArrayRead,L2TagArrayRead} {
    l2m_profileMiss;
    l11m_profileMiss;
    a1_allocateL1D;
    i0_invCluster;
    ii_invIcache;
    nM_issueRdBlkM;
    p_popMandatoryQueue;
  }
  transition(Es, C0_Load_L1miss, Es_F0) {L1D0TagArrayRead, L2TagArrayRead, L2DataArrayRead} {  // can this be folded with S_F?
     l10m_profileMiss;
    a0_allocateL1D;
    f0_L2ToL1;
    p_popMandatoryQueue;
  }

  transition(Es, C1_Load_L1miss, Es_F1) {L1D1TagArrayRead, L2TagArrayRead, L2DataArrayRead} {  // can this be folded with S_F?
     l11m_profileMiss;
    a1_allocateL1D;
    f1_L2ToL1;
    p_popMandatoryQueue;
  }

  transition(Es, Ifetch0_L1miss, S0) {L1ITagArrayRead, L2TagArrayRead} {
      l1im_profileMiss;
     i2_invL2;
    ai_allocateL1I;
    a2_allocateL2;
    ib_invBothClusters;
    nS_issueRdBlkS;
    p_popMandatoryQueue;
  }

  transition(Es, Ifetch1_L1miss, S1) {L1ITagArrayRead, L2TagArrayRead} {
     l1im_profileMiss;
    i2_invL2;
    ai_allocateL1I;
    a2_allocateL2;
    ib_invBothClusters;
    nS_issueRdBlkS;
    p_popMandatoryQueue;
  }

  // THES SHOULD NOT BE INSTANTANEOUS BUT OH WELL FOR NOW
  transition(Es, {C0_Store_L1hit, C0_Store_L1miss}, M0) {L1D0TagArrayWrite,L1D0TagArrayRead, L2TagArrayRead, L1D0DataArrayWrite, L2TagArrayWrite, L2DataArrayWrite} {
    a0_allocateL1D;
    i1_invCluster;
    s0_storeDone;   // instantaneous L1/L2 dirty - no writethrough delay
    p_popMandatoryQueue;
  }

  transition(Es, {C1_Store_L1hit, C1_Store_L1miss}, M1) {L1D1TagArrayRead, L1D1TagArrayWrite, L1D1DataArrayWrite, L2TagArrayWrite, L2DataArrayWrite} {
    a1_allocateL1D;
    i0_invCluster;
    s1_storeDone;
    p_popMandatoryQueue;
  }

  transition(E0, C0_Load_L1miss, E0_F) {L1D0TagArrayRead, L2TagArrayRead, L2DataArrayRead} {
     l10m_profileMiss;
    a0_allocateL1D;
    f0_L2ToL1;
    p_popMandatoryQueue;
  }

  transition(E0, C1_Load_L1miss, E0_Es) {L1D0TagArrayRead, L2TagArrayRead, L2DataArrayRead} {
     l11m_profileMiss;
    a1_allocateL1D;
    f1_L2ToL1;
    p_popMandatoryQueue;
  }

  transition(E0, Ifetch0_L1miss, S0) {L2TagArrayRead, L1ITagArrayRead} {
    l2m_profileMiss; // permissions miss, still issue RdBlkS
    l1im_profileMiss;
    i2_invL2;
    ai_allocateL1I;
    a2_allocateL2;
    i0_invCluster;
    nS_issueRdBlkS;
    p_popMandatoryQueue;
  }

  transition(E0, Ifetch1_L1miss, S1) {L2TagArrayRead, L1ITagArrayRead } {
    l2m_profileMiss; // permissions miss, still issue RdBlkS
    l1im_profileMiss;
    i2_invL2;
    ai_allocateL1I;
    a2_allocateL2;
    i0_invCluster;
    nS_issueRdBlkS;
    p_popMandatoryQueue;
  }

  transition(E0, {C0_Store_L1hit, C0_Store_L1miss}, M0) {L1D0TagArrayRead, L1D0DataArrayWrite, L1D0TagArrayWrite, L2TagArrayRead, L2DataArrayWrite, L2TagArrayWrite} {
    a0_allocateL1D;
    s0_storeDone;
    p_popMandatoryQueue;
  }

  transition(E0, C1_Store_L1miss, M1) {L1D0TagArrayRead, L1D0TagArrayWrite, L2TagArrayRead, L2TagArrayWrite, L2DataArrayWrite} {
    a1_allocateL1D;
    l11m_profileMiss;
    i0_invCluster;
    s1_storeDone;
    p_popMandatoryQueue;
  }

  transition(E1, C1_Load_L1miss, E1_F) {L1D1TagArrayRead, L2TagArrayRead, L2DataArrayRead} {
    a1_allocateL1D;
    l11m_profileMiss;
    f1_L2ToL1;
    p_popMandatoryQueue;
  }

  transition(E1, C0_Load_L1miss, E1_Es) {L1D0TagArrayRead, L2TagArrayRead, L2DataArrayRead} {
    a0_allocateL1D;
    l10m_profileMiss;
    f0_L2ToL1;
    p_popMandatoryQueue;
  }

  transition(E1, Ifetch1_L1miss, S1) {L2TagArrayRead, L1ITagArrayRead} {
    l2m_profileMiss; // permissions miss, still issue RdBlkS
    l1im_profileMiss;
    i2_invL2;
    ai_allocateL1I;
    a2_allocateL2;
    i1_invCluster;
    nS_issueRdBlkS;
    p_popMandatoryQueue;
  }

  transition(E1, Ifetch0_L1miss, S0) {L2TagArrayRead,L1ITagArrayRead} {
    l2m_profileMiss; // permissions miss, still issue RdBlkS
    l1im_profileMiss;
    i2_invL2;
    ai_allocateL1I;
    a2_allocateL2;
    i1_invCluster;
    nS_issueRdBlkS;
    p_popMandatoryQueue;
  }

  transition(E1, {C1_Store_L1hit, C1_Store_L1miss}, M1) {L1D1TagArrayRead, L1D1TagArrayWrite, L2TagArrayRead, L2DataArrayWrite, L2TagArrayWrite} {
    a1_allocateL1D;
    s1_storeDone;
    p_popMandatoryQueue;
  }

  transition(E1, C0_Store_L1miss, M0) {L1D0TagArrayRead, L1D0TagArrayWrite, L2TagArrayRead, L2TagArrayWrite, L2DataArrayWrite} {
     l10m_profileMiss;
    a0_allocateL1D;
    i1_invCluster;
    s0_storeDone;
    p_popMandatoryQueue;
  }

  transition({O}, {C0_Store_L1hit, C0_Store_L1miss}, O_M0) {L1D0TagArrayRead, L2TagArrayRead} {
    l2m_profileMiss; // permissions miss, still issue CtoD
     l10m_profileMiss;
    a0_allocateL1D;
    i1_invCluster;
    ii_invIcache;
    nM_issueRdBlkM;
    p_popMandatoryQueue;
  }

  transition({O}, {C1_Store_L1hit, C1_Store_L1miss}, O_M1) {L1D1TagArrayRead, L2TagArrayRead} {
    l2m_profileMiss; // permissions miss, still issue RdBlkS
    l11m_profileMiss;
    a1_allocateL1D;
    i0_invCluster;
    ii_invIcache;
    nM_issueRdBlkM;
    p_popMandatoryQueue;
  }

  transition(O, C0_Load_L1miss, O_F0) {L2TagArrayRead, L2DataArrayRead, L1D0TagArrayRead} {
    l10m_profileMiss;
    a0_allocateL1D;
    f0_L2ToL1;
    p_popMandatoryQueue;
  }

  transition(O, C1_Load_L1miss, O_F1) {L2TagArrayRead, L2DataArrayRead, L1D1TagArrayRead} {
    l11m_profileMiss;
    a1_allocateL1D;
    f1_L2ToL1;
    p_popMandatoryQueue;
  }

  transition(Ms, C0_Load_L1miss, Ms_F0) {L2TagArrayRead, L2DataArrayRead, L1D0TagArrayRead} {
    l10m_profileMiss;
    a0_allocateL1D;
    f0_L2ToL1;
    p_popMandatoryQueue;
  }

  transition(Ms, C1_Load_L1miss, Ms_F1) {L2TagArrayRead, L2DataArrayRead, L1D1TagArrayRead} {
    l11m_profileMiss;
    a1_allocateL1D;
    f1_L2ToL1;
    p_popMandatoryQueue;
  }

  transition({Ms, M0, M1, O}, Ifetch0_L1miss, MO_S0) {L1ITagArrayRead,  L2TagArrayRead} {
    l2m_profileMiss;  // permissions miss
    l1im_profileMiss;
    ai_allocateL1I;
    t_allocateTBE;
    ib_invBothClusters;
    vd_victim;
//    i2_invL2;
    p_popMandatoryQueue;
  }

  transition({Ms, M0, M1, O}, Ifetch1_L1miss, MO_S1) {L1ITagArrayRead L2TagArrayRead } {
    l2m_profileMiss;  // permissions miss
    l10m_profileMiss;
    ai_allocateL1I;
    t_allocateTBE;
    ib_invBothClusters;
    vd_victim;
//    i2_invL2;
    p_popMandatoryQueue;
  }

  transition(Ms, {C0_Store_L1hit, C0_Store_L1miss}, M0) {L1D0TagArrayRead, L1D0TagArrayWrite, L1D0DataArrayWrite, L2TagArrayRead, L2DataArrayWrite, L2TagArrayWrite} {
    a0_allocateL1D;
    i1_invCluster;
    s0_storeDone;
    p_popMandatoryQueue;
  }

  transition(Ms, {C1_Store_L1hit, C1_Store_L1miss}, M1) {L1D1TagArrayRead, L1D1TagArrayWrite, L1D1DataArrayWrite, L2TagArrayRead, L2DataArrayWrite, L2TagArrayWrite} {
    a1_allocateL1D;
    i0_invCluster;
    s1_storeDone;
    p_popMandatoryQueue;
  }

  transition(M0, C0_Load_L1miss, M0_F) {L1D0TagArrayRead, L2TagArrayRead, L2DataArrayRead} {
   l10m_profileMiss;
    a0_allocateL1D;
    f0_L2ToL1;
    p_popMandatoryQueue;
  }

  transition(M0, C1_Load_L1miss, M0_Ms) {L2TagArrayRead, L2DataArrayRead,L1D1TagArrayRead} {
   l11m_profileMiss;
    a1_allocateL1D;
    f1_L2ToL1;
    p_popMandatoryQueue;
  }

  transition(M0, {C0_Store_L1hit, C0_Store_L1miss}) {L1D0TagArrayRead, L1D0DataArrayWrite, L2DataArrayWrite, L2TagArrayRead} {
    a0_allocateL1D;
    s0_storeDone;
    p_popMandatoryQueue;
  }

  transition(M0, {C1_Store_L1hit, C1_Store_L1miss}, M1) {L1D0TagArrayRead, L1D0TagArrayWrite, L1D0DataArrayWrite, L2DataArrayWrite, L2TagArrayRead, L2TagArrayWrite} {
    a1_allocateL1D;
    i0_invCluster;
    s1_storeDone;
    p_popMandatoryQueue;
  }

  transition(M1, C0_Load_L1miss, M1_Ms) {L2TagArrayRead, L2DataArrayRead, L1D0TagArrayRead} {
    l10m_profileMiss;
    a0_allocateL1D;
    f0_L2ToL1;
    p_popMandatoryQueue;
  }

  transition(M1, C1_Load_L1miss, M1_F) {L1D1TagArrayRead L2TagArrayRead, L2DataArrayRead} {
   l11m_profileMiss;
    a1_allocateL1D;
    f1_L2ToL1;
    p_popMandatoryQueue;
  }

  transition(M1, {C0_Store_L1hit, C0_Store_L1miss}, M0) {L1D0TagArrayRead, L1D0TagArrayWrite, L1D0DataArrayWrite, L2TagArrayRead, L2DataArrayWrite, L2TagArrayWrite} {
    a0_allocateL1D;
    i1_invCluster;
    s0_storeDone;
    p_popMandatoryQueue;
  }

  transition(M1, {C1_Store_L1hit, C1_Store_L1miss}) {L1D1TagArrayRead, L1D1DataArrayWrite, L2TagArrayRead, L2DataArrayWrite} {
    a1_allocateL1D;
    s1_storeDone;
    p_popMandatoryQueue;
  }

  // end transitions from base

  // Begin simple hit transitions
  transition({S, Es, E0, O, Ms, M0, O_F1, S_F1, Si_F0, Si_F1, Es_F1, E0_Es,
          Ms_F1, M0_Ms}, C0_Load_L1hit) {L1D0TagArrayRead, L1D0DataArrayRead} {
    // track hits, if implemented
    l0_loadDone;
    p_popMandatoryQueue;
  }

  transition({S, Es, E1, O, Ms, M1, O_F0, S_F0, Si_F0, Si_F1, Es_F0, E1_Es,
          Ms_F0, M1_Ms}, C1_Load_L1hit) {L1D1TagArrayRead, L1D1DataArrayRead} {
    // track hits, if implemented
    l1_loadDone;
    p_popMandatoryQueue;
  }

  transition({S, S_C, S_F0, S_F1, S_F}, Ifetch0_L1hit) {L1ITagArrayRead, L1IDataArrayRead} {
    // track hits, if implemented
    il0_loadDone;
    p_popMandatoryQueue;
  }

  transition({S, S_C, S_F0, S_F1, S_F}, Ifetch1_L1hit) {L1ITagArrayRead, L1IDataArrayWrite} {
    // track hits, if implemented
    il1_loadDone;
    p_popMandatoryQueue;
  }

  // end simple hit transitions

  // Transitions from transient states

  // recycles
  transition({I_M0, I_M0M1, I_M1M0, I_M0Ms, I_M1Ms, I_E0S, I_ES, IF_E0S, IF_ES,
          IF0_ES, IF1_ES, S_F0, S_F, O_F0, O_F, S_M0, O_M0, Es_F0, Es_F, E0_F,
          E1_Es, Ms_F0, Ms_F, M0_F, M1_Ms}, C0_Load_L1hit) {} {
    zz_recycleMandatoryQueue;
  }

  transition({IF_E1S, F_S0, F_S1, ES_I, MO_I, MO_S0, MO_S1, Si_F0, Si_F1, S_M1,
          O_M1, S0, S1, I_C, S0_C, S1_C, S_C}, C0_Load_L1miss) {} {
    zz_recycleMandatoryQueue;
  }

  transition({I_M1, I_M0M1, I_M1M0, I_M0Ms, I_M1Ms, I_E1S, I_ES, IF_E1S, IF_ES,
          IF0_ES, IF1_ES, S_F1, S_F, O_F1, O_F, S_M1, O_M1, Es_F1, Es_F, E1_F,
          E0_Es, Ms_F1, Ms_F, M0_Ms, M1_F}, C1_Load_L1hit) {} {
    zz_recycleMandatoryQueue;
  }

  transition({IF_E0S, F_S0, F_S1, ES_I, MO_I, MO_S0, MO_S1, Si_F0, Si_F1, S_M0,
          O_M0, S0, S1, I_C, S0_C, S1_C, S_C},  C1_Load_L1miss) {} {
    zz_recycleMandatoryQueue;
  }

  transition({F_S0, F_S1, MO_S0, MO_S1, Si_F0, Si_F1, S0, S1, S0_C, S1_C}, {Ifetch0_L1hit, Ifetch1_L1hit}) {} {
    zz_recycleMandatoryQueue;
  }

  transition({I_M0, I_M1, I_M0M1, I_M1M0, I_M0Ms, I_M1Ms, I_E0S, I_E1S, I_ES,
          IF_E0S, IF_E1S, IF_ES, IF0_ES, IF1_ES, ES_I, MO_I, S_F0, S_F1, S_F,
          O_F0, O_F1, O_F, S_M0, S_M1, O_M0, O_M1, Es_F0, Es_F1, Es_F, E0_F,
          E1_F, E0_Es, E1_Es, Ms_F0, Ms_F1, Ms_F, M0_F, M0_Ms, M1_F, M1_Ms, I_C,
          S_C}, {Ifetch0_L1miss, Ifetch1_L1miss}) {} {
    zz_recycleMandatoryQueue;
  }

  transition({I_E1S, IF_E1S, F_S0, F_S1, ES_I, MO_I, MO_S0, MO_S1, S_F1, O_F1,
          Si_F0, Si_F1, S_M1, O_M1, S0, S1, Es_F1, E1_F, E0_Es, Ms_F1, M0_Ms,
          M1_F, I_C, S0_C, S1_C, S_C}, {C0_Store_L1miss}) {} {
    zz_recycleMandatoryQueue;
  }

  transition({I_E0S, IF_E0S, F_S0, F_S1, ES_I, MO_I, MO_S0, MO_S1 S_F0, O_F0,
          Si_F0, Si_F1, S_M0, O_M0, S0, S1, Es_F0, E0_F, E1_Es, Ms_F0, M0_F,
          M1_Ms, I_C, S0_C, S1_C, S_C}, {C1_Store_L1miss}) {} {
    zz_recycleMandatoryQueue;
  }

  transition({I_M0, I_M0M1, I_M1M0, I_M0Ms, I_M1Ms, I_E0S, I_ES, IF_E0S, IF_ES,
          IF0_ES, IF1_ES, S_F0, S_F1, S_F, O_F0, O_F1, O_F, Si_F0, Si_F1, S_M0, O_M0, Es_F0, Es_F1, Es_F, E0_F, E0_Es, E1_Es, Ms_F0, Ms_F1, Ms_F, M0_F, M0_Ms, M1_Ms}, {C0_Store_L1hit}) {} {
    zz_recycleMandatoryQueue;
  }

  transition({I_M1, I_M0M1, I_M1M0, I_M0Ms, I_M1Ms, I_E1S, I_ES, IF_E1S, IF_ES,
          IF0_ES, IF1_ES, S_F0, S_F1, S_F, O_F0, O_F1, O_F, Si_F0, Si_F1, S_M1,
          O_M1, Es_F0, Es_F1, Es_F, E1_F, E0_Es, E1_Es, Ms_F0, Ms_F1, Ms_F,
          M0_Ms, M1_F, M1_Ms}, {C1_Store_L1hit}) {} {
    zz_recycleMandatoryQueue;
  }

  transition({I_M0, I_M0M1, I_M1M0, I_M0Ms, I_M1Ms, I_E0S, I_ES, IF_E0S, IF_ES,
          IF0_ES, IF1_ES, S_F0, S_F, O_F0, O_F, S_M0, O_M0, Es_F0, Es_F, E0_F,
          E1_Es, Ms_F0, Ms_F, M0_F, M1_Ms}, L1D0_Repl) {} {
    zz_recycleMandatoryQueue;
  }

  transition({I_M1, I_M0M1, I_M1M0, I_M0Ms, I_M1Ms, I_E1S, I_ES, IF_E1S, IF_ES,
          IF0_ES, IF1_ES, S_F1, S_F, O_F1, O_F, S_M1, O_M1, Es_F1, Es_F, E1_F,
          E0_Es, Ms_F1, Ms_F, M0_Ms, M1_F}, L1D1_Repl) {} {
    zz_recycleMandatoryQueue;
  }

  transition({F_S0, F_S1, MO_S0, MO_S1, Si_F0, Si_F1, S0, S1, S0_C, S1_C}, L1I_Repl) {} {
    zz_recycleMandatoryQueue;
  }

  transition({S_C, S0_C, S1_C, S0, S1, Si_F0, Si_F1, I_M0, I_M1, I_M0M1, I_M1M0, I_M0Ms, I_M1Ms, I_E0S, I_E1S, I_ES, S_F0, S_F1, S_F, O_F0, O_F1, O_F, S_M0, O_M0, S_M1, O_M1, Es_F0, Es_F1, Es_F, E0_F, E1_F, E0_Es, E1_Es, Ms_F0, Ms_F1, Ms_F, M0_F, M0_Ms, M1_F, M1_Ms, MO_S0, MO_S1, IF_E0S, IF_E1S, IF_ES, IF0_ES, IF1_ES, F_S0, F_S1}, L2_Repl) {} {
    zz_recycleMandatoryQueue;
  }

  transition({IF_E0S, IF_E1S, IF_ES, IF0_ES, IF1_ES, F_S0, F_S1}, {NB_AckS,
          PrbInvData, PrbInvDataDemand, PrbInv, PrbShrData, PrbShrDataDemand}) {} {
    zz_recycleMandatoryQueue;  // these should be resolved soon, but I didn't want to add more states, though technically they could be solved now, and probes really could be solved but i don't think it's really necessary.
  }

  transition({IF_E0S, IF_E1S, IF_ES, IF0_ES, IF1_ES}, NB_AckE) {} {
    zz_recycleMandatoryQueue;  // these should be resolved soon, but I didn't want to add more states, though technically they could be solved now, and probes really could be solved but i don't think it's really necessary.
  }

  transition({E0_Es, E1_F, Es_F1}, C0_Load_L1miss, Es_F) {L2DataArrayRead} {
    l10m_profileMiss;
    a0_allocateL1D;
    f0_L2ToL1;
    p_popMandatoryQueue;
  }

  transition(S_F1, C0_Load_L1miss, S_F) {L2DataArrayRead} {
    l10m_profileMiss;
    a0_allocateL1D;
    f0_L2ToL1;
    p_popMandatoryQueue;
  }

  transition(O_F1, C0_Load_L1miss, O_F) {L2DataArrayRead} {
    l10m_profileMiss;
    a0_allocateL1D;
    f0_L2ToL1;
    p_popMandatoryQueue;
  }

  transition({Ms_F1, M0_Ms, M1_F}, C0_Load_L1miss, Ms_F) {L2DataArrayRead} {
    l10m_profileMiss;
    a0_allocateL1D;
    f0_L2ToL1;
    p_popMandatoryQueue;
  }

  transition(I_M0, C1_Load_L1miss, I_M0Ms){
    l11m_profileMiss;
    l2m_profileMiss;
    a1_allocateL1D;
    p_popMandatoryQueue;
  }

  transition(I_M1, C0_Load_L1miss, I_M1Ms){
    l10m_profileMiss;
    l2m_profileMiss;
    a0_allocateL1D;
    p_popMandatoryQueue;
  }

  transition(I_M0, C1_Store_L1miss, I_M0M1) {
    l11m_profileMiss;
    l2m_profileMiss;
    a1_allocateL1D;
    p_popMandatoryQueue;
  }

  transition(I_M1, C0_Store_L1miss, I_M1M0) {L1D0TagArrayRead, L1D0TagArrayWrite, L2TagArrayRead, L2TagArrayWrite} {
    l2m_profileMiss;
    a0_allocateL1D;
    p_popMandatoryQueue;
  }

  transition(I_E0S, C1_Load_L1miss, I_ES) {} {
    l2m_profileMiss;
    l11m_profileMiss;
    a1_allocateL1D;
    p_popMandatoryQueue;
  }

  transition(I_E1S, C0_Load_L1miss, I_ES) {} {
    l2m_profileMiss;
    l10m_profileMiss;
    l2m_profileMiss;
    a0_allocateL1D;
    p_popMandatoryQueue;
  }

  transition({E1_Es, E0_F, Es_F0}, C1_Load_L1miss, Es_F) {L2DataArrayRead} {
    l11m_profileMiss;
    a1_allocateL1D;
    f1_L2ToL1;
    p_popMandatoryQueue;
  }

  transition(S_F0, C1_Load_L1miss, S_F) { L2DataArrayRead} {
    l11m_profileMiss;
    a1_allocateL1D;
    f1_L2ToL1;
    p_popMandatoryQueue;
  }

  transition(O_F0, C1_Load_L1miss, O_F) {L2DataArrayRead} {
    l11m_profileMiss;
    a1_allocateL1D;
    f1_L2ToL1;
    p_popMandatoryQueue;
  }

  transition({Ms_F0, M1_Ms, M0_F}, C1_Load_L1miss, Ms_F) {L2DataArrayRead} {
    l11m_profileMiss;
    a1_allocateL1D;
    f1_L2ToL1;
    p_popMandatoryQueue;
  }

  transition({S, Es, E0, O, Ms, M0, O_F1, S_F1, Si_F0, Si_F1, Es_F1, E0_Es, Ms_F1, M0_Ms}, L1D0_Repl) {L1D0TagArrayRead} {
    i0_invCluster;
  }

  transition({S, Es, E1, O, Ms, M1, O_F0, S_F0, Si_F0, Si_F1, Es_F0, E1_Es, Ms_F0, M1_Ms}, L1D1_Repl) {L1D1TagArrayRead} {
    i1_invCluster;
  }

  transition({S, S_C, S_F0, S_F1}, L1I_Repl) {L1ITagArrayRead} {
    ii_invIcache;
  }

  transition({S, E0, E1, Es}, L2_Repl, ES_I) {L2TagArrayRead,L1D0TagArrayRead, L1D1TagArrayRead, L1ITagArrayRead} {
    forward_eviction_to_cpu0;
    forward_eviction_to_cpu1;
    t_allocateTBE;
    vc_victim;
    ib_invBothClusters;
    i2_invL2;
    ii_invIcache;
  }

  transition({Ms, M0, M1, O}, L2_Repl, MO_I) {L2TagArrayRead, L2TagArrayWrite, L1D0TagArrayRead, L1D1TagArrayRead} {
    forward_eviction_to_cpu0;
    forward_eviction_to_cpu1;
    t_allocateTBE;
    vd_victim;
    i2_invL2;
    ib_invBothClusters;  // nothing will happen for D0 on M1, vice versa
  }

  transition(S0, NB_AckS, S) {L1D0DataArrayWrite, L1D0TagArrayWrite, L2DataArrayWrite, L2TagArrayWrite} {
    wi_writeIcache;
    xi0_loadDone;
    uu_sendUnblock;
    sdv_sendDoneValid;
    pr_popResponseQueue;
  }

  transition(S1, NB_AckS, S) {L1D1DataArrayWrite, L1D1TagArrayWrite, L2DataArrayWrite, L2TagArrayWrite} {
    wi_writeIcache;
    xi1_loadDone;
    sdv_sendDoneValid;
    uu_sendUnblock;
    pr_popResponseQueue;
  }

  transition(S0_C, NB_AckS, S_C) { L1IDataArrayWrite,L2DataArrayWrite} {
    // does not need send done since the rdblks was "sinked"
    wi_writeIcache;
    xi0_loadDone;
    uu_sendUnblock;
    pr_popResponseQueue;
  }

  transition(S1_C, NB_AckS, S_C) { L1D1DataArrayWrite,L2DataArrayWrite} {
    wi_writeIcache;
    xi1_loadDone;
    uu_sendUnblock;
    pr_popResponseQueue;
  }

  transition(I_M0, NB_AckM, M0) { L1D0DataArrayWrite, L1D0TagArrayWrite, L2DataArrayWrite, L2TagArrayWrite} {
    w0_writeDcache;
    xs0_storeDone;
    sdv_sendDoneValid;
    uu_sendUnblock;
    pr_popResponseQueue;
  }

  transition(I_M1, NB_AckM, M1) {L1D1DataArrayWrite, L1D1TagArrayWrite,L2DataArrayWrite, L2TagArrayWrite} {
    w1_writeDcache;
    xs1_storeDone;
    sdv_sendDoneValid;
    uu_sendUnblock;
    pr_popResponseQueue;
  }

  // THESE MO->M1 should not be instantaneous but oh well for now.
  transition(I_M0M1, NB_AckM, M1) {L1D1DataArrayWrite, L1D1TagArrayWrite,L2DataArrayWrite, L2TagArrayWrite} {
    w0_writeDcache;
    xs0_storeDone;
    sdv_sendDoneValid;
    uu_sendUnblock;
    i0_invCluster;
    s1_storeDone;
    pr_popResponseQueue;
  }

  transition(I_M1M0, NB_AckM, M0) {L1D0DataArrayWrite, L1D0TagArrayWrite,L2DataArrayWrite, L2TagArrayWrite} {
    w1_writeDcache;
    xs1_storeDone;
    sdv_sendDoneValid;
    uu_sendUnblock;
    i1_invCluster;
    s0_storeDone;
    pr_popResponseQueue;
  }

  // Above shoudl be more like this, which has some latency to xfer to L1
  transition(I_M0Ms, NB_AckM, M0_Ms) {L1D0DataArrayWrite,L2DataArrayWrite} {
    w0_writeDcache;
    xs0_storeDone;
    sdv_sendDoneValid;
    uu_sendUnblock;
    f1_L2ToL1;
    pr_popResponseQueue;
  }

  transition(I_M1Ms, NB_AckM, M1_Ms) {L1D1DataArrayWrite,L2DataArrayWrite} {
    w1_writeDcache;
    xs1_storeDone;
    sdv_sendDoneValid;
    uu_sendUnblock;
    f0_L2ToL1;
    pr_popResponseQueue;
  }

  transition(I_E0S, NB_AckE, E0) {L1D0DataArrayWrite, L2DataArrayWrite, L2TagArrayWrite} {
    w0_writeDcache;
    xl0_loadDone;
    sdv_sendDoneValid;
    uu_sendUnblock;
    pr_popResponseQueue;
  }

  transition(I_E1S, NB_AckE, E1) {L1D1DataArrayWrite, L1D1TagArrayWrite, L2DataArrayWrite, L2TagArrayWrite} {
    w1_writeDcache;
    xl1_loadDone;
    sdv_sendDoneValid;
    uu_sendUnblock;
    pr_popResponseQueue;
  }

  transition(I_ES, NB_AckE, Es) {L1D1DataArrayWrite, L1D1TagArrayWrite, L1D0DataArrayWrite, L1D0TagArrayWrite, L2DataArrayWrite, L2TagArrayWrite } {
    w0_writeDcache;
    xl0_loadDone;
    w1_writeDcache;
    xl1_loadDone;
    sdv_sendDoneValid;
    uu_sendUnblock;
    pr_popResponseQueue;
  }

  transition(I_E0S, NB_AckS, S) {L1D0DataArrayWrite, L1D0TagArrayWrite, L2DataArrayWrite, L2TagArrayWrite} {
    w0_writeDcache;
    xl0_loadDone;
    sdv_sendDoneValid;
    uu_sendUnblock;
    pr_popResponseQueue;
  }

  transition(I_E1S, NB_AckS, S) {L1D1TagArrayWrite, L1D1DataArrayWrite, L2TagArrayWrite, L2DataArrayWrite} {
    w1_writeDcache;
    xl1_loadDone;
    sdv_sendDoneValid;
    uu_sendUnblock;
    pr_popResponseQueue;
  }

  transition(I_ES, NB_AckS, S) {L1D0TagArrayWrite, L1D0DataArrayWrite, L2TagArrayWrite,  L2DataArrayWrite} {
    w0_writeDcache;
    xl0_loadDone;
    w1_writeDcache;
    xl1_loadDone;
    sdv_sendDoneValid;
    uu_sendUnblock;
    pr_popResponseQueue;
  }

  transition(S_F0, L2_to_L1D0, S) {L1D0TagArrayWrite, L1D0DataArrayWrite,  L2TagArrayWrite, L2DataArrayRead} {
    c0_copyL2ToL1;
    l0_loadDone;
    pt_popTriggerQueue;
  }

  transition(S_F1, L2_to_L1D1, S) {L1D1TagArrayWrite, L1D1DataArrayWrite,  L2TagArrayWrite, L2DataArrayRead} {
    c1_copyL2ToL1;
    l1_loadDone;
    pt_popTriggerQueue;
  }

  transition(Si_F0, L2_to_L1I, S) {L1ITagArrayWrite, L1IDataArrayWrite,  L2TagArrayWrite, L2DataArrayRead} {
    ci_copyL2ToL1;
    il0_loadDone;
    pt_popTriggerQueue;
  }

  transition(Si_F1, L2_to_L1I, S) {L1ITagArrayWrite, L1IDataArrayWrite,  L2TagArrayWrite, L2DataArrayRead} {
    ci_copyL2ToL1;
    il1_loadDone;
    pt_popTriggerQueue;
  }

  transition(S_F, L2_to_L1D0, S_F1) { L1D0DataArrayWrite, L2DataArrayRead} {
    c0_copyL2ToL1;
    l0_loadDone;
    pt_popTriggerQueue;
  }

  transition(S_F, L2_to_L1D1, S_F0) { L1D1DataArrayWrite, L2DataArrayRead} {
    c1_copyL2ToL1;
    l1_loadDone;
    pt_popTriggerQueue;
  }

  transition(O_F0, L2_to_L1D0, O) { L1D0DataArrayWrite, L1D0TagArrayWrite, L2TagArrayWrite, L2DataArrayRead} {
    c0_copyL2ToL1;
    l0_loadDone;
    pt_popTriggerQueue;
  }

  transition(O_F1, L2_to_L1D1, O) {L1D1DataArrayWrite, L1D1TagArrayWrite, L2TagArrayWrite, L2DataArrayRead} {
    c1_copyL2ToL1;
    l1_loadDone;
    pt_popTriggerQueue;
  }

  transition(O_F, L2_to_L1D0, O_F1) { L1D0DataArrayWrite, L2DataArrayRead} {
    c0_copyL2ToL1;
    l0_loadDone;
    pt_popTriggerQueue;
  }

  transition(O_F, L2_to_L1D1, O_F0) { L1D1DataArrayWrite, L2DataArrayRead} {
    c1_copyL2ToL1;
    l1_loadDone;
    pt_popTriggerQueue;
  }

  transition(M1_F, L2_to_L1D1, M1) {L1D1DataArrayWrite, L1D1TagArrayWrite, L2TagArrayWrite, L2DataArrayRead} {
    c1_copyL2ToL1;
    l1_loadDone;
    pt_popTriggerQueue;
  }

  transition(M0_F, L2_to_L1D0, M0) {L1D0DataArrayWrite, L1D0TagArrayWrite, L2TagArrayWrite, L2DataArrayRead} {
    c0_copyL2ToL1;
    l0_loadDone;
    pt_popTriggerQueue;
  }

  transition(Ms_F0, L2_to_L1D0, Ms) {L1D0DataArrayWrite, L1D0TagArrayWrite, L2TagArrayWrite, L2DataArrayRead} {
    c0_copyL2ToL1;
    l0_loadDone;
    pt_popTriggerQueue;
  }

  transition(Ms_F1, L2_to_L1D1, Ms) {L1D1DataArrayWrite, L1D1TagArrayWrite, L2TagArrayWrite, L2DataArrayRead} {
    c1_copyL2ToL1;
    l1_loadDone;
    pt_popTriggerQueue;
  }

  transition(Ms_F, L2_to_L1D0, Ms_F1) {L1D0DataArrayWrite, L2DataArrayRead} {
    c0_copyL2ToL1;
    l0_loadDone;
    pt_popTriggerQueue;
  }

  transition(Ms_F, L2_to_L1D1, Ms_F0) {L1IDataArrayWrite, L2DataArrayRead} {
    c1_copyL2ToL1;
    l1_loadDone;
    pt_popTriggerQueue;
  }

  transition(M1_Ms, L2_to_L1D0, Ms) {L1D0TagArrayWrite, L1D0DataArrayWrite, L2TagArrayWrite, L2DataArrayRead} {
    c0_copyL2ToL1;
    l0_loadDone;
    pt_popTriggerQueue;
  }

  transition(M0_Ms, L2_to_L1D1, Ms) {L1D1TagArrayWrite, L1D1DataArrayWrite, L2TagArrayWrite, L2DataArrayRead} {
    c1_copyL2ToL1;
    l1_loadDone;
    pt_popTriggerQueue;
  }

  transition(Es_F0, L2_to_L1D0, Es) {L1D0TagArrayWrite, L1D0DataArrayWrite, L2TagArrayWrite, L2DataArrayRead} {
    c0_copyL2ToL1;
    l0_loadDone;
    pt_popTriggerQueue;
  }

  transition(Es_F1, L2_to_L1D1, Es) {L1D1TagArrayWrite, L1D1DataArrayWrite, L2TagArrayWrite, L2DataArrayRead} {
    c1_copyL2ToL1;
    l1_loadDone;
    pt_popTriggerQueue;
  }

  transition(Es_F, L2_to_L1D0, Es_F1) {L2TagArrayRead, L2DataArrayRead} {
    c0_copyL2ToL1;
    l0_loadDone;
    pt_popTriggerQueue;
  }

  transition(Es_F, L2_to_L1D1, Es_F0) {L2TagArrayRead, L2DataArrayRead} {
    c1_copyL2ToL1;
    l1_loadDone;
    pt_popTriggerQueue;
  }

  transition(E0_F, L2_to_L1D0, E0) {L2TagArrayRead, L2DataArrayRead} {
    c0_copyL2ToL1;
    l0_loadDone;
    pt_popTriggerQueue;
  }

  transition(E1_F, L2_to_L1D1, E1) {L2TagArrayRead, L2DataArrayRead} {
    c1_copyL2ToL1;
    l1_loadDone;
    pt_popTriggerQueue;
  }

  transition(E1_Es, L2_to_L1D0, Es) {L2TagArrayRead, L2DataArrayRead} {
    c0_copyL2ToL1;
    l0_loadDone;
    pt_popTriggerQueue;
  }

  transition(E0_Es, L2_to_L1D1, Es) {L2TagArrayRead, L2DataArrayRead} {
    c1_copyL2ToL1;
    l1_loadDone;
    pt_popTriggerQueue;
  }

  transition(IF_E0S, L2_to_L1D0, I_E0S) {} {
    pt_popTriggerQueue;
  }

  transition(IF_E1S, L2_to_L1D1, I_E1S) {} {
    pt_popTriggerQueue;
  }

  transition(IF_ES, L2_to_L1D0, IF1_ES) {} {
    pt_popTriggerQueue;
  }

  transition(IF_ES, L2_to_L1D1, IF0_ES) {} {
    pt_popTriggerQueue;
  }

  transition(IF0_ES, L2_to_L1D0, I_ES) {} {
    pt_popTriggerQueue;
  }

  transition(IF1_ES, L2_to_L1D1, I_ES) {} {
    pt_popTriggerQueue;
  }

  transition(F_S0, L2_to_L1I, S0) {} {
    pt_popTriggerQueue;
  }

  transition(F_S1, L2_to_L1I, S1) {} {
    pt_popTriggerQueue;
  }

  transition({S_M0, O_M0}, NB_AckM, M0) {L1D0TagArrayWrite, L1D0DataArrayWrite, L2DataArrayWrite, L2TagArrayWrite} {
    xs0_storeDone;
    sdv_sendDoneValid;
    uu_sendUnblock;
    pr_popResponseQueue;
  }

  transition({S_M1, O_M1}, NB_AckM, M1) {L1D1TagArrayWrite, L1D1DataArrayWrite, L2DataArrayWrite, L2TagArrayWrite} {
    xs1_storeDone;
    sdv_sendDoneValid;
    uu_sendUnblock;
    pr_popResponseQueue;
  }

  transition(MO_I, NB_AckWB, I) {L2TagArrayWrite} {
    wb_data;
    ra_sendReplAck;
    sdi_sendDoneInvalid;
    d_deallocateTBE;
    pr_popResponseQueue;
  }

  transition(ES_I, NB_AckWB, I) {L2TagArrayWrite} {
    wb_data;
    ra_sendReplAck;
    sdi_sendDoneInvalid;
    d_deallocateTBE;
    pr_popResponseQueue;
  }

  transition(MO_S0, NB_AckWB, S0) {L2TagArrayWrite} {
    wb_data;
    i2_invL2;
    a2_allocateL2;
    sdv_sendDoneValid;
    nS_issueRdBlkS;
    d_deallocateTBE; // FOO
    pr_popResponseQueue;
  }

  transition(MO_S1, NB_AckWB, S1) {L2TagArrayWrite} {
    wb_data;
    i2_invL2;
    a2_allocateL2;
    sdv_sendDoneValid;
    nS_issueRdBlkS;
    d_deallocateTBE; // FOO
    pr_popResponseQueue;
  }

  // Writeback cancel "ack"
  transition(I_C, NB_AckWB, I) {L2TagArrayWrite} {
    ss_sendStaleNotification;
    sdi_sendDoneInvalid;
    d_deallocateTBE;
    pr_popResponseQueue;
  }

  transition(S0_C, NB_AckWB, S0) {L2TagArrayWrite} {
    ss_sendStaleNotification;
    sdv_sendDoneValid;
    pr_popResponseQueue;
  }

  transition(S1_C, NB_AckWB, S1) {L2TagArrayWrite} {
    ss_sendStaleNotification;
    sdv_sendDoneValid;
    pr_popResponseQueue;
  }

  transition(S_C, NB_AckWB, S) {L2TagArrayWrite} {
    ss_sendStaleNotification;
    sdv_sendDoneValid;
    pr_popResponseQueue;
  }

  // Begin Probe Transitions

  transition({Ms, M0, M1, O}, {PrbInvData, PrbInvDataDemand}, I) {L2TagArrayRead, L2TagArrayWrite, L2DataArrayRead} {
    forward_eviction_to_cpu0;
    forward_eviction_to_cpu1;
    pd_sendProbeResponseData;
    i2_invL2;
    ib_invBothClusters;
    pp_popProbeQueue;
  }

  transition({Es, E0, E1, S, I}, {PrbInvData, PrbInvDataDemand}, I) {L2TagArrayRead, L2TagArrayWrite} {
    forward_eviction_to_cpu0;
    forward_eviction_to_cpu1;
    pi_sendProbeResponseInv;
    i2_invL2;
    ib_invBothClusters;
    ii_invIcache;  // only relevant for S
    pp_popProbeQueue;
  }

  transition(S_C, {PrbInvData, PrbInvDataDemand}, I_C) {L2TagArrayWrite} {
    t_allocateTBE;
    forward_eviction_to_cpu0;
    forward_eviction_to_cpu1;
    pi_sendProbeResponseInv;
    i2_invL2;
    ib_invBothClusters;
    ii_invIcache;
    pp_popProbeQueue;
  }

  transition(I_C, {PrbInvData, PrbInvDataDemand}, I_C) {} {
    pi_sendProbeResponseInv;
    ib_invBothClusters;
    pp_popProbeQueue;
  }

  transition({Ms, M0, M1, O, Es, E0, E1, S, I}, PrbInv, I) {L2TagArrayRead, L2TagArrayWrite} {
    forward_eviction_to_cpu0;
    forward_eviction_to_cpu1;
    pi_sendProbeResponseInv;
    i2_invL2; // nothing will happen in I
    ib_invBothClusters;
    ii_invIcache;
    pp_popProbeQueue;
  }

  transition(S_C, PrbInv, I_C) {L2TagArrayWrite} {
    t_allocateTBE;
    forward_eviction_to_cpu0;
    forward_eviction_to_cpu1;
    pi_sendProbeResponseInv;
    i2_invL2;
    ib_invBothClusters;
    ii_invIcache;
    pp_popProbeQueue;
  }

  transition(I_C, PrbInv, I_C) {} {
    pi_sendProbeResponseInv;
    ib_invBothClusters;
    ii_invIcache;
    pp_popProbeQueue;
  }

  transition({Ms, M0, M1, O}, {PrbShrData, PrbShrDataDemand}, O) {L2TagArrayRead, L2TagArrayWrite, L2DataArrayRead} {
    pd_sendProbeResponseData;
    pp_popProbeQueue;
  }

  transition({Es, E0, E1, S}, {PrbShrData, PrbShrDataDemand}, S) {L2TagArrayRead, L2TagArrayWrite} {
    ph_sendProbeResponseHit;
    pp_popProbeQueue;
  }

  transition(S_C, {PrbShrData, PrbShrDataDemand}) {} {
    ph_sendProbeResponseHit;
    pp_popProbeQueue;
  }

  transition({I, I_C}, {PrbShrData, PrbShrDataDemand}) {L2TagArrayRead} {
    pb_sendProbeResponseBackprobe;
    pp_popProbeQueue;
  }

  transition({I_M0, I_E0S}, {PrbInv, PrbInvData, PrbInvDataDemand}) {} {
    pi_sendProbeResponseInv;
    ib_invBothClusters;  // must invalidate current data (only relevant for I_M0)
    a0_allocateL1D;  // but make sure there is room for incoming data when it arrives
    pp_popProbeQueue;
  }

  transition({I_M1, I_E1S}, {PrbInv, PrbInvData, PrbInvDataDemand}) {} {
    pi_sendProbeResponseInv;
    ib_invBothClusters; // must invalidate current data (only relevant for I_M1)
    a1_allocateL1D;  // but make sure there is room for incoming data when it arrives
    pp_popProbeQueue;
  }

  transition({I_M0M1, I_M1M0, I_M0Ms, I_M1Ms, I_ES}, {PrbInv, PrbInvData, PrbInvDataDemand, PrbShrData, PrbShrDataDemand}) {} {
    pi_sendProbeResponseInv;
    ib_invBothClusters;
    a0_allocateL1D;
    a1_allocateL1D;
    pp_popProbeQueue;
  }

  transition({I_M0, I_E0S, I_M1, I_E1S}, {PrbShrData, PrbShrDataDemand}) {} {
    pb_sendProbeResponseBackprobe;
    pp_popProbeQueue;
  }

  transition(ES_I, {PrbInvData, PrbInvDataDemand}, I_C) {} {
    pi_sendProbeResponseInv;
    ib_invBothClusters;
    ii_invIcache;
    pp_popProbeQueue;
  }

  transition(MO_I, {PrbInvData, PrbInvDataDemand}, I_C) {} {
    pdt_sendProbeResponseDataFromTBE;
    ib_invBothClusters;
    ii_invIcache;
    pp_popProbeQueue;
  }

  transition(MO_I, PrbInv, I_C) {} {
    pi_sendProbeResponseInv;
    ib_invBothClusters;
    ii_invIcache;
    pp_popProbeQueue;
  }

  transition(ES_I, PrbInv, I_C) {} {
    pi_sendProbeResponseInv;
    ib_invBothClusters;
    ii_invIcache;
    pp_popProbeQueue;
  }

  transition(ES_I, {PrbShrData, PrbShrDataDemand}, ES_I) {} {
    ph_sendProbeResponseHit;
    s_setSharedFlip;
    pp_popProbeQueue;
  }

  transition(MO_I, {PrbShrData, PrbShrDataDemand}, MO_I) {} {
    pdt_sendProbeResponseDataFromTBE;
    s_setSharedFlip;
    pp_popProbeQueue;
  }

  transition(MO_S0, {PrbInvData, PrbInvDataDemand}, S0_C) {L2TagArrayWrite} {
    forward_eviction_to_cpu0;
    forward_eviction_to_cpu1;
    pdt_sendProbeResponseDataFromTBE;
    i2_invL2;
    a2_allocateL2;
    nS_issueRdBlkS;
    d_deallocateTBE;
    pp_popProbeQueue;
  }

  transition(MO_S1, {PrbInvData, PrbInvDataDemand}, S1_C) {} {
    forward_eviction_to_cpu0;
    forward_eviction_to_cpu1;
    pdt_sendProbeResponseDataFromTBE;
    i2_invL2;
    a2_allocateL2;
    nS_issueRdBlkS;
    d_deallocateTBE;
    pp_popProbeQueue;
  }

  transition(MO_S0, PrbInv, S0_C) {L2TagArrayWrite} {
    forward_eviction_to_cpu0;
    forward_eviction_to_cpu1;
    pi_sendProbeResponseInv;
    i2_invL2;
    a2_allocateL2;
    nS_issueRdBlkS;
    d_deallocateTBE;
    pp_popProbeQueue;
  }

  transition(MO_S1, PrbInv, S1_C) {L2TagArrayWrite} {
    forward_eviction_to_cpu0;
    forward_eviction_to_cpu1;
    pi_sendProbeResponseInv;
    i2_invL2;
    a2_allocateL2;
    nS_issueRdBlkS;
    d_deallocateTBE;
    pp_popProbeQueue;
  }

  transition({MO_S0, MO_S1}, {PrbShrData, PrbShrDataDemand}) {} {
    pdt_sendProbeResponseDataFromTBE;
    s_setSharedFlip;
    pp_popProbeQueue;
  }

  transition({S_F0, Es_F0, E0_F, E1_Es}, {PrbInvData, PrbInvDataDemand, PrbInv}, IF_E0S) {} {
    forward_eviction_to_cpu0;
    forward_eviction_to_cpu1;
    pi_sendProbeResponseInv;
    // invalidate everything you've got
    ib_invBothClusters;
    ii_invIcache;
    i2_invL2;
    // but make sure you have room for what you need from the fill
    a0_allocateL1D;
    a2_allocateL2;
    n_issueRdBlk;
    pp_popProbeQueue;
  }

  transition({S_F1, Es_F1, E1_F, E0_Es}, {PrbInvData, PrbInvDataDemand, PrbInv}, IF_E1S) {} {
    forward_eviction_to_cpu0;
    forward_eviction_to_cpu1;
    pi_sendProbeResponseInv;
    // invalidate everything you've got
    ib_invBothClusters;
    ii_invIcache;
    i2_invL2;
    // but make sure you have room for what you need from the fill
    a1_allocateL1D;
    a2_allocateL2;
    n_issueRdBlk;
    pp_popProbeQueue;
  }

  transition({S_F, Es_F}, {PrbInvData, PrbInvDataDemand, PrbInv}, IF_ES) {} {
    forward_eviction_to_cpu0;
    forward_eviction_to_cpu1;
    pi_sendProbeResponseInv;
    // invalidate everything you've got
    ib_invBothClusters;
    ii_invIcache;
    i2_invL2;
    // but make sure you have room for what you need from the fill
    a0_allocateL1D;
    a1_allocateL1D;
    a2_allocateL2;
    n_issueRdBlk;
    pp_popProbeQueue;
  }

  transition(Si_F0, {PrbInvData, PrbInvDataDemand, PrbInv}, F_S0) {} {
    forward_eviction_to_cpu0;
    forward_eviction_to_cpu1;
    pi_sendProbeResponseInv;
    ib_invBothClusters;
    ii_invIcache;
    i2_invL2;
    ai_allocateL1I;
    a2_allocateL2;
    nS_issueRdBlkS;
    pp_popProbeQueue;
  }

  transition(Si_F1, {PrbInvData, PrbInvDataDemand, PrbInv}, F_S1) {} {
    forward_eviction_to_cpu0;
    forward_eviction_to_cpu1;
    pi_sendProbeResponseInv;
    ib_invBothClusters;
    ii_invIcache;
    i2_invL2;
    ai_allocateL1I;
    a2_allocateL2;
    nS_issueRdBlkS;
    pp_popProbeQueue;
  }

  transition({Es_F0, E0_F, E1_Es}, {PrbShrData, PrbShrDataDemand}, S_F0) {} {
    ph_sendProbeResponseHit;
    pp_popProbeQueue;
  }

  transition({Es_F1, E1_F, E0_Es}, {PrbShrData, PrbShrDataDemand}, S_F1) {} {
    ph_sendProbeResponseHit;
    pp_popProbeQueue;
  }

  transition(Es_F, {PrbShrData, PrbShrDataDemand}, S_F) {} {
    ph_sendProbeResponseHit;
    pp_popProbeQueue;
  }

  transition({S_F0, S_F1, S_F, Si_F0, Si_F1}, {PrbShrData, PrbShrDataDemand}) {} {
    ph_sendProbeResponseHit;
    pp_popProbeQueue;
  }

  transition(S_M0, {PrbInvData, PrbInvDataDemand}, I_M0) {} {
    forward_eviction_to_cpu0;
    forward_eviction_to_cpu1;
    pim_sendProbeResponseInvMs;
    ib_invBothClusters;
    ii_invIcache;
    i2_invL2;
    a0_allocateL1D;
    a2_allocateL2;
    pp_popProbeQueue;
  }

  transition(O_M0, {PrbInvData, PrbInvDataDemand}, I_M0) {L2DataArrayRead} {
    forward_eviction_to_cpu0;
    forward_eviction_to_cpu1;
    pdm_sendProbeResponseDataMs;
    ib_invBothClusters;
    ii_invIcache;
    i2_invL2;
    a0_allocateL1D;
    a2_allocateL2;
    pp_popProbeQueue;
  }

  transition({S_M0, O_M0}, {PrbInv}, I_M0) {} {
    forward_eviction_to_cpu0;
    forward_eviction_to_cpu1;
    pim_sendProbeResponseInvMs;
    ib_invBothClusters;
    ii_invIcache;
    i2_invL2;
    a0_allocateL1D;
    a2_allocateL2;
    pp_popProbeQueue;
  }

  transition(S_M1, {PrbInvData, PrbInvDataDemand}, I_M1) {} {
    forward_eviction_to_cpu0;
    forward_eviction_to_cpu1;
    pim_sendProbeResponseInvMs;
    ib_invBothClusters;
    ii_invIcache;
    i2_invL2;
    a1_allocateL1D;
    a2_allocateL2;
    pp_popProbeQueue;
  }

  transition(O_M1, {PrbInvData, PrbInvDataDemand}, I_M1) {} {
    forward_eviction_to_cpu0;
    forward_eviction_to_cpu1;
    pdm_sendProbeResponseDataMs;
    ib_invBothClusters;
    ii_invIcache;
    i2_invL2;
    a1_allocateL1D;
    a2_allocateL2;
    pp_popProbeQueue;
  }

  transition({S_M1, O_M1}, {PrbInv}, I_M1) {} {
    forward_eviction_to_cpu0;
    forward_eviction_to_cpu1;
    pim_sendProbeResponseInvMs;
    ib_invBothClusters;
    ii_invIcache;
    i2_invL2;
    a1_allocateL1D;
    a2_allocateL2;
    pp_popProbeQueue;
  }

  transition({S0, S0_C}, {PrbInvData, PrbInvDataDemand, PrbInv}) {} {
    forward_eviction_to_cpu0;
    forward_eviction_to_cpu1;
    pi_sendProbeResponseInv;
    ib_invBothClusters;
    ii_invIcache;
    i2_invL2;
    ai_allocateL1I;
    a2_allocateL2;
    pp_popProbeQueue;
  }

  transition({S1, S1_C}, {PrbInvData, PrbInvDataDemand, PrbInv}) {} {
    forward_eviction_to_cpu0;
    forward_eviction_to_cpu1;
    pi_sendProbeResponseInv;
    ib_invBothClusters;
    ii_invIcache;
    i2_invL2;
    ai_allocateL1I;
    a2_allocateL2;
    pp_popProbeQueue;
  }

  transition({S_M0, S_M1}, {PrbShrData, PrbShrDataDemand}) {} {
    ph_sendProbeResponseHit;
    pp_popProbeQueue;
  }

  transition({O_M0, O_M1}, {PrbShrData, PrbShrDataDemand}) {L2DataArrayRead} {
    pd_sendProbeResponseData;
    pp_popProbeQueue;
  }

  transition({S0, S1, S0_C, S1_C}, {PrbShrData, PrbShrDataDemand}) {} {
    pb_sendProbeResponseBackprobe;
    pp_popProbeQueue;
  }

  transition({Ms_F0, M0_F, M1_Ms, O_F0}, {PrbInvData, PrbInvDataDemand}, IF_E0S) {L2DataArrayRead} {
    forward_eviction_to_cpu0;
    forward_eviction_to_cpu1;
    pd_sendProbeResponseData;
    ib_invBothClusters;
    i2_invL2;
    a0_allocateL1D;
    a2_allocateL2;
    n_issueRdBlk;
    pp_popProbeQueue;
  }

  transition({Ms_F1, M1_F, M0_Ms, O_F1}, {PrbInvData, PrbInvDataDemand}, IF_E1S) {L2DataArrayRead} {
    forward_eviction_to_cpu0;
    forward_eviction_to_cpu1;
    pd_sendProbeResponseData;
    ib_invBothClusters;
    i2_invL2;
    a1_allocateL1D;
    a2_allocateL2;
    n_issueRdBlk;
    pp_popProbeQueue;
  }

  transition({Ms_F, O_F}, {PrbInvData, PrbInvDataDemand}, IF_ES) {L2DataArrayRead} {
    forward_eviction_to_cpu0;
    forward_eviction_to_cpu1;
    pd_sendProbeResponseData;
    ib_invBothClusters;
    i2_invL2;
    a0_allocateL1D;
    a1_allocateL1D;
    a2_allocateL2;
    n_issueRdBlk;
    pp_popProbeQueue;
  }

  transition({Ms_F0, M0_F, M1_Ms, O_F0}, PrbInv, IF_E0S) {} {
    forward_eviction_to_cpu0;
    forward_eviction_to_cpu1;
    pi_sendProbeResponseInv;
    ib_invBothClusters;
    i2_invL2;
    a0_allocateL1D;
    a2_allocateL2;
    n_issueRdBlk;
    pp_popProbeQueue;
  }

  transition({Ms_F1, M1_F, M0_Ms, O_F1}, PrbInv, IF_E1S) {} {
    forward_eviction_to_cpu0;
    forward_eviction_to_cpu1;
    pi_sendProbeResponseInv;
    ib_invBothClusters;
    i2_invL2;
    a1_allocateL1D;
    a2_allocateL2;
    n_issueRdBlk;
    pp_popProbeQueue;
  }

  transition({Ms_F, O_F}, PrbInv, IF_ES) {} {
    forward_eviction_to_cpu0;
    forward_eviction_to_cpu1;
    pi_sendProbeResponseInv;
    ib_invBothClusters;
    i2_invL2;
    a0_allocateL1D;
    a1_allocateL1D;
    a2_allocateL2;
    n_issueRdBlk;
    pp_popProbeQueue;
  }

  transition({Ms_F0, M0_F, M1_Ms}, {PrbShrData, PrbShrDataDemand}, O_F0) {L2DataArrayRead} {
    pd_sendProbeResponseData;
    pp_popProbeQueue;
  }

  transition({Ms_F1, M1_F, M0_Ms}, {PrbShrData, PrbShrDataDemand}, O_F1) {} {
  }

  transition({Ms_F}, {PrbShrData, PrbShrDataDemand}, O_F) {L2DataArrayRead} {
    pd_sendProbeResponseData;
    pp_popProbeQueue;
  }

  transition({O_F0, O_F1, O_F}, {PrbShrData, PrbShrDataDemand}) {L2DataArrayRead} {
    pd_sendProbeResponseData;
    pp_popProbeQueue;
  }

  // END TRANSITIONS
}


