/* * Copyright (c) 2009-2012 Mark D. Hill and David A. Wood * Copyright (c) 2010-2012 Advanced Micro Devices, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer; * redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution; * neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ machine(MachineType:L1Cache, "MI Example L1 Cache") : Sequencer * sequencer; CacheMemory * cacheMemory; Cycles cache_response_latency := 12; Cycles issue_latency := 2; bool send_evictions; // NETWORK BUFFERS MessageBuffer * requestFromCache, network="To", virtual_network="2", vnet_type="request"; MessageBuffer * responseFromCache, network="To", virtual_network="4", vnet_type="response"; MessageBuffer * forwardToCache, network="From", virtual_network="3", vnet_type="forward"; MessageBuffer * responseToCache, network="From", virtual_network="4", vnet_type="response"; MessageBuffer * mandatoryQueue; { // STATES state_declaration(State, desc="Cache states") { I, AccessPermission:Invalid, desc="Not Present/Invalid"; II, AccessPermission:Busy, desc="Not Present/Invalid, issued PUT"; M, AccessPermission:Read_Write, desc="Modified"; MI, AccessPermission:Busy, desc="Modified, issued PUT"; MII, AccessPermission:Busy, desc="Modified, issued PUTX, received nack"; IS, AccessPermission:Busy, desc="Issued request for LOAD/IFETCH"; IM, AccessPermission:Busy, desc="Issued request for STORE/ATOMIC"; } // EVENTS enumeration(Event, desc="Cache events") { // From processor Load, desc="Load request from processor"; Ifetch, desc="Ifetch request from processor"; Store, desc="Store request from processor"; Data, desc="Data from network"; Fwd_GETX, desc="Forward from network"; Inv, desc="Invalidate request from dir"; Replacement, desc="Replace a block"; Writeback_Ack, desc="Ack from the directory for a writeback"; Writeback_Nack, desc="Nack from the directory for a writeback"; } // STRUCTURE DEFINITIONS // CacheEntry structure(Entry, desc="...", interface="AbstractCacheEntry") { State CacheState, desc="cache state"; bool Dirty, desc="Is the data dirty (different than memory)?"; DataBlock DataBlk, desc="Data in the block"; } // TBE fields structure(TBE, desc="...") { State TBEState, desc="Transient state"; DataBlock DataBlk, desc="data for the block, required for concurrent writebacks"; } structure(TBETable, external="yes") { TBE lookup(Addr); void allocate(Addr); void deallocate(Addr); bool isPresent(Addr); } // STRUCTURES TBETable TBEs, template="", constructor="m_number_of_TBEs"; // PROTOTYPES Tick clockEdge(); Cycles ticksToCycles(Tick t); void set_cache_entry(AbstractCacheEntry a); void unset_cache_entry(); void set_tbe(TBE b); void unset_tbe(); void profileMsgDelay(int virtualNetworkType, Cycles b); MachineID mapAddressToMachine(Addr addr, MachineType mtype); Entry getCacheEntry(Addr address), return_by_pointer="yes" { return static_cast(Entry, "pointer", cacheMemory.lookup(address)); } // FUNCTIONS Event mandatory_request_type_to_event(RubyRequestType type) { if (type == RubyRequestType:LD) { return Event:Load; } else if (type == RubyRequestType:IFETCH) { return Event:Ifetch; } else if ((type == RubyRequestType:ST) || (type == RubyRequestType:ATOMIC)) { return Event:Store; } else { error("Invalid RubyRequestType"); } } 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; } else { 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[addr]; if(is_valid(tbe)) { return L1Cache_State_to_permission(tbe.TBEState); } Entry cache_entry := getCacheEntry(addr); if(is_valid(cache_entry)) { return L1Cache_State_to_permission(cache_entry.CacheState); } return AccessPermission:NotPresent; } void setAccessPermission(Entry cache_entry, Addr addr, State state) { if (is_valid(cache_entry)) { cache_entry.changePermission(L1Cache_State_to_permission(state)); } } void functionalRead(Addr addr, Packet *pkt) { TBE tbe := TBEs[addr]; if(is_valid(tbe)) { testAndRead(addr, tbe.DataBlk, pkt); } else { testAndRead(addr, getCacheEntry(addr).DataBlk, pkt); } } int functionalWrite(Addr addr, Packet *pkt) { int num_functional_writes := 0; TBE tbe := TBEs[addr]; if(is_valid(tbe)) { num_functional_writes := num_functional_writes + testAndWrite(addr, tbe.DataBlk, pkt); return num_functional_writes; } num_functional_writes := num_functional_writes + testAndWrite(addr, getCacheEntry(addr).DataBlk, pkt); return num_functional_writes; } // NETWORK PORTS out_port(requestNetwork_out, RequestMsg, requestFromCache); out_port(responseNetwork_out, ResponseMsg, responseFromCache); in_port(forwardRequestNetwork_in, RequestMsg, forwardToCache) { if (forwardRequestNetwork_in.isReady(clockEdge())) { peek(forwardRequestNetwork_in, RequestMsg, block_on="addr") { Entry cache_entry := getCacheEntry(in_msg.addr); TBE tbe := TBEs[in_msg.addr]; if (in_msg.Type == CoherenceRequestType:GETX) { trigger(Event:Fwd_GETX, in_msg.addr, cache_entry, tbe); } else if (in_msg.Type == CoherenceRequestType:WB_ACK) { trigger(Event:Writeback_Ack, in_msg.addr, cache_entry, tbe); } else if (in_msg.Type == CoherenceRequestType:WB_NACK) { trigger(Event:Writeback_Nack, in_msg.addr, cache_entry, tbe); } else if (in_msg.Type == CoherenceRequestType:INV) { trigger(Event:Inv, in_msg.addr, cache_entry, tbe); } else { error("Unexpected message"); } } } } in_port(responseNetwork_in, ResponseMsg, responseToCache) { if (responseNetwork_in.isReady(clockEdge())) { peek(responseNetwork_in, ResponseMsg, block_on="addr") { Entry cache_entry := getCacheEntry(in_msg.addr); TBE tbe := TBEs[in_msg.addr]; if (in_msg.Type == CoherenceResponseType:DATA) { trigger(Event:Data, in_msg.addr, cache_entry, tbe); } else { error("Unexpected message"); } } } } // 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); if (is_invalid(cache_entry) && cacheMemory.cacheAvail(in_msg.LineAddress) == false ) { // make room for the block trigger(Event:Replacement, cacheMemory.cacheProbe(in_msg.LineAddress), getCacheEntry(cacheMemory.cacheProbe(in_msg.LineAddress)), TBEs[cacheMemory.cacheProbe(in_msg.LineAddress)]); } else { trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.LineAddress, cache_entry, TBEs[in_msg.LineAddress]); } } } } // ACTIONS action(a_issueRequest, "a", desc="Issue a request") { enqueue(requestNetwork_out, RequestMsg, issue_latency) { out_msg.addr := address; out_msg.Type := CoherenceRequestType:GETX; out_msg.Requestor := machineID; out_msg.Destination.add(mapAddressToMachine(address, MachineType:Directory)); out_msg.MessageSize := MessageSizeType:Control; } } action(b_issuePUT, "b", desc="Issue a PUT request") { enqueue(requestNetwork_out, RequestMsg, issue_latency) { assert(is_valid(cache_entry)); out_msg.addr := address; out_msg.Type := CoherenceRequestType:PUTX; out_msg.Requestor := machineID; out_msg.Destination.add(mapAddressToMachine(address, MachineType:Directory)); out_msg.DataBlk := cache_entry.DataBlk; out_msg.MessageSize := MessageSizeType:Data; } } action(e_sendData, "e", desc="Send data from cache to requestor") { peek(forwardRequestNetwork_in, RequestMsg) { enqueue(responseNetwork_out, ResponseMsg, cache_response_latency) { assert(is_valid(cache_entry)); out_msg.addr := address; out_msg.Type := CoherenceResponseType:DATA; out_msg.Sender := machineID; out_msg.Destination.add(in_msg.Requestor); out_msg.DataBlk := cache_entry.DataBlk; out_msg.MessageSize := MessageSizeType:Response_Data; } } } action(ee_sendDataFromTBE, "\e", desc="Send data from TBE to requestor") { peek(forwardRequestNetwork_in, RequestMsg) { enqueue(responseNetwork_out, ResponseMsg, cache_response_latency) { assert(is_valid(tbe)); out_msg.addr := address; out_msg.Type := CoherenceResponseType:DATA; out_msg.Sender := machineID; out_msg.Destination.add(in_msg.Requestor); out_msg.DataBlk := tbe.DataBlk; out_msg.MessageSize := MessageSizeType:Response_Data; } } } action(i_allocateL1CacheBlock, "i", desc="Allocate a cache block") { if (is_valid(cache_entry)) { } else { set_cache_entry(cacheMemory.allocate(address, new Entry)); } } action(h_deallocateL1CacheBlock, "h", desc="deallocate a cache block") { if (is_valid(cache_entry)) { cacheMemory.deallocate(address); unset_cache_entry(); } } action(m_popMandatoryQueue, "m", desc="Pop the mandatory request queue") { mandatoryQueue_in.dequeue(clockEdge()); } action(n_popResponseQueue, "n", desc="Pop the response queue") { Tick delay := responseNetwork_in.dequeue(clockEdge()); profileMsgDelay(1, ticksToCycles(delay)); } action(o_popForwardedRequestQueue, "o", desc="Pop the forwarded request queue") { Tick delay := forwardRequestNetwork_in.dequeue(clockEdge()); profileMsgDelay(2, ticksToCycles(delay)); } action(p_profileMiss, "pi", desc="Profile cache miss") { ++cacheMemory.demand_misses; } action(p_profileHit, "ph", desc="Profile cache miss") { ++cacheMemory.demand_hits; } action(r_load_hit, "r", desc="Notify sequencer the load completed.") { assert(is_valid(cache_entry)); DPRINTF(RubySlicc,"%s\n", cache_entry.DataBlk); cacheMemory.setMRU(cache_entry); sequencer.readCallback(address, cache_entry.DataBlk, false); } action(rx_load_hit, "rx", desc="External load completed.") { peek(responseNetwork_in, ResponseMsg) { assert(is_valid(cache_entry)); DPRINTF(RubySlicc,"%s\n", cache_entry.DataBlk); cacheMemory.setMRU(cache_entry); sequencer.readCallback(address, cache_entry.DataBlk, true, machineIDToMachineType(in_msg.Sender)); } } action(s_store_hit, "s", desc="Notify sequencer that store completed.") { assert(is_valid(cache_entry)); DPRINTF(RubySlicc,"%s\n", cache_entry.DataBlk); cacheMemory.setMRU(cache_entry); sequencer.writeCallback(address, cache_entry.DataBlk, false); } action(sx_store_hit, "sx", desc="External store completed.") { peek(responseNetwork_in, ResponseMsg) { assert(is_valid(cache_entry)); DPRINTF(RubySlicc,"%s\n", cache_entry.DataBlk); cacheMemory.setMRU(cache_entry); sequencer.writeCallback(address, cache_entry.DataBlk, true, machineIDToMachineType(in_msg.Sender)); } } action(u_writeDataToCache, "u", desc="Write data to the cache") { peek(responseNetwork_in, ResponseMsg) { assert(is_valid(cache_entry)); cache_entry.DataBlk := in_msg.DataBlk; } } action(forward_eviction_to_cpu, "\cc", desc="sends eviction information to the processor") { if (send_evictions) { DPRINTF(RubySlicc, "Sending invalidation for %#x to the CPU\n", address); sequencer.evictionCallback(address); } } action(v_allocateTBE, "v", desc="Allocate TBE") { TBEs.allocate(address); set_tbe(TBEs[address]); } action(w_deallocateTBE, "w", desc="Deallocate TBE") { TBEs.deallocate(address); unset_tbe(); } action(x_copyDataFromCacheToTBE, "x", desc="Copy data from cache to TBE") { assert(is_valid(cache_entry)); assert(is_valid(tbe)); tbe.DataBlk := cache_entry.DataBlk; } action(z_stall, "z", desc="stall") { // do nothing } // TRANSITIONS transition({IS, IM, MI, II, MII}, {Load, Ifetch, Store, Replacement}) { z_stall; } transition({IS, IM}, {Fwd_GETX, Inv}) { z_stall; } transition(MI, Inv) { o_popForwardedRequestQueue; } transition(M, Store) { s_store_hit; p_profileHit; m_popMandatoryQueue; } transition(M, {Load, Ifetch}) { r_load_hit; p_profileHit; m_popMandatoryQueue; } transition(I, Inv) { o_popForwardedRequestQueue; } transition(I, Store, IM) { v_allocateTBE; i_allocateL1CacheBlock; a_issueRequest; p_profileMiss; m_popMandatoryQueue; } transition(I, {Load, Ifetch}, IS) { v_allocateTBE; i_allocateL1CacheBlock; a_issueRequest; p_profileMiss; m_popMandatoryQueue; } transition(IS, Data, M) { u_writeDataToCache; rx_load_hit; w_deallocateTBE; n_popResponseQueue; } transition(IM, Data, M) { u_writeDataToCache; sx_store_hit; w_deallocateTBE; n_popResponseQueue; } transition(M, Fwd_GETX, I) { e_sendData; forward_eviction_to_cpu; o_popForwardedRequestQueue; } transition(I, Replacement) { h_deallocateL1CacheBlock; } transition(M, {Replacement,Inv}, MI) { v_allocateTBE; b_issuePUT; x_copyDataFromCacheToTBE; forward_eviction_to_cpu; h_deallocateL1CacheBlock; } transition(MI, Writeback_Ack, I) { w_deallocateTBE; o_popForwardedRequestQueue; } transition(MI, Fwd_GETX, II) { ee_sendDataFromTBE; o_popForwardedRequestQueue; } transition(MI, Writeback_Nack, MII) { o_popForwardedRequestQueue; } transition(MII, Fwd_GETX, I) { ee_sendDataFromTBE; w_deallocateTBE; o_popForwardedRequestQueue; } transition(II, Writeback_Nack, I) { w_deallocateTBE; o_popForwardedRequestQueue; } }