2f30950143
We eventually plan to replace the m5 cache hierarchy with the GEMS hierarchy, but for now we will make both live alongside eachother.
799 lines
28 KiB
Text
799 lines
28 KiB
Text
|
|
/*
|
|
* Copyright (c) 1999-2005 Mark D. Hill and David A. Wood
|
|
* 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.
|
|
*/
|
|
|
|
/*
|
|
* $Id$
|
|
*
|
|
*/
|
|
|
|
|
|
machine(L1Cache, "MSI Directory L1 Cache CMP") {
|
|
|
|
// NODE L1 CACHE
|
|
// From this node's L1 cache TO the network
|
|
// a local L1 -> this L2 bank, currently ordered with directory forwarded requests
|
|
MessageBuffer requestFromL1Cache, network="To", virtual_network="0", ordered="true";
|
|
MessageBuffer dummyFrom1, network="To", virtual_network="1", ordered="false"; // dummy buffer that shouldn't be used
|
|
MessageBuffer dummyFrom2, network="To", virtual_network="2", ordered="false"; // dummy buffer that shouldn't be used
|
|
// a local L1 -> this L2 bank
|
|
MessageBuffer responseFromL1Cache, network="To", virtual_network="3", ordered="false";
|
|
MessageBuffer dummyFrom4, network="To", virtual_network="4", ordered="false"; // dummy buffer that shouldn't be used
|
|
|
|
|
|
// To this node's L1 cache FROM the network
|
|
MessageBuffer dummyTo0, network="From", virtual_network="0", ordered="false"; // dummy buffer that shouldn't be used
|
|
MessageBuffer dummyTo1, network="From", virtual_network="1", ordered="false"; // dummy buffer that shouldn't be used
|
|
// a L2 bank -> this L1
|
|
MessageBuffer requestToL1Cache, network="From", virtual_network="2", ordered="true";
|
|
// a L2 bank -> this L1
|
|
MessageBuffer responseToL1Cache, network="From", virtual_network="3", ordered="false";
|
|
MessageBuffer dummyTo4, network="From", virtual_network="4", ordered="false"; // dummy buffer that shouldn't be used
|
|
|
|
// STATES
|
|
enumeration(State, desc="Cache states", default="L1Cache_State_L1_I") {
|
|
// Base states
|
|
NP, desc="Not present in either cache";
|
|
L1_I, desc="a L1 cache entry Idle";
|
|
L1_S, desc="a L1 cache entry Shared";
|
|
L1_M, desc="a L1 cache entry Modified", format="!b";
|
|
|
|
// Transient States
|
|
L1_IS, desc="L1 idle, issued GETS, have not seen response yet";
|
|
L1_ISI, desc="L1 idle, issued GETS, saw INV, still waiting for data";
|
|
L1_IM, desc="L1 idle, issued GETX, have not seen response yet";
|
|
L1_IMI, desc="L1 idle, issued GETX, saw INV, still waiting for data";
|
|
L1_IMS, desc="L1 idle, issued GETX, saw DownGrade, still waiting for data";
|
|
L1_IMSI, desc="L1 idle, issued GETX, saw DownGrade, saw INV, still waiting for data";
|
|
|
|
L1_SI, desc="issued PUTS, waiting for response";
|
|
L1_MI, desc="issued PUTX, waiting for response";
|
|
}
|
|
|
|
// EVENTS
|
|
enumeration(Event, desc="Cache events") {
|
|
// L1 events
|
|
Load, desc="Load request from the home processor";
|
|
Ifetch, desc="I-fetch request from the home processor";
|
|
Store, desc="Store request from the home processor";
|
|
|
|
// L1 is required to send response to the L2 immediately
|
|
L1_INV, "INV", desc="L1 Invalidation of M data", format="!r";
|
|
L1_INV_S, "INV", desc="L1 Invalidation of S data", format="!r";
|
|
L1_DownGrade, "Force DownGrade", desc="L2 cache forces an L1 cache in M to downgrade to S and writeback result";
|
|
|
|
// receiving of data
|
|
L1_Data, "Data", desc="Data in response to an L1 request, transistion to M or S depending on request";
|
|
L1_Data_S, "Data S", desc="Data in response to an L1 request, write data then transistion to S";
|
|
L1_Data_I, "Data I", desc="Data in response to an L1 request, write data then transistion to I";
|
|
|
|
// receiving of acks
|
|
L1_PutAck, "Put Ack", desc="PutS or PutX ack from L2";
|
|
|
|
// internal generated request
|
|
// L1 request to replace block, results in either a PUTS or PUTX request
|
|
L1_Replacement, desc="L1 Replacement", format="!r";
|
|
// Currently same as replacement, request initiated when block is in the wrong L1 cache
|
|
L1_WriteBack, desc="on-chip L1 cache must write back to shared L2";
|
|
}
|
|
|
|
// TYPES
|
|
|
|
// CacheEntry
|
|
structure(Entry, desc="...", interface="AbstractCacheEntry" ) {
|
|
State CacheState, desc="cache state";
|
|
DataBlock DataBlk, desc="data for the block";
|
|
}
|
|
|
|
// TBE fields
|
|
structure(TBE, desc="...") {
|
|
Address Address, desc="Physical address for this TBE";
|
|
State TBEState, desc="Transient state";
|
|
DataBlock DataBlk, desc="Buffer for the data block";
|
|
bool isPrefetch, desc="Set if this was caused by a prefetch";
|
|
}
|
|
|
|
external_type(CacheMemory) {
|
|
bool cacheAvail(Address);
|
|
Address cacheProbe(Address);
|
|
void allocate(Address);
|
|
void deallocate(Address);
|
|
Entry lookup(Address);
|
|
void changePermission(Address, AccessPermission);
|
|
bool isTagPresent(Address);
|
|
}
|
|
|
|
external_type(TBETable) {
|
|
TBE lookup(Address);
|
|
void allocate(Address);
|
|
void deallocate(Address);
|
|
bool isPresent(Address);
|
|
}
|
|
|
|
TBETable L1_TBEs, template_hack="<L1Cache_TBE>";
|
|
|
|
CacheMemory L1IcacheMemory, template_hack="<L1Cache_Entry>", constructor_hack='L1_CACHE_NUM_SETS_BITS,L1_CACHE_ASSOC,MachineType_L1Cache,int_to_string(i)+"_L1I"', abstract_chip_ptr="true";
|
|
CacheMemory L1DcacheMemory, template_hack="<L1Cache_Entry>", constructor_hack='L1_CACHE_NUM_SETS_BITS,L1_CACHE_ASSOC,MachineType_L1Cache,int_to_string(i)+"_L1D"', abstract_chip_ptr="true";
|
|
|
|
MessageBuffer mandatoryQueue, ordered="false", rank="100", abstract_chip_ptr="true";
|
|
// the optionalQueue doesn't have to be ordered for correctness
|
|
// however inforcing order ensures the prefetches reach the L2 in order
|
|
MessageBuffer optionalQueue, ordered="true", rank="101", abstract_chip_ptr="true";
|
|
|
|
Sequencer sequencer, abstract_chip_ptr="true", constructor_hack="i";
|
|
|
|
int cache_state_to_int(State state);
|
|
|
|
// inclusive cache returns L1 entries only
|
|
Entry getL1CacheEntry(Address addr), return_by_ref="yes" {
|
|
if (L1DcacheMemory.isTagPresent(addr)) {
|
|
return L1DcacheMemory[addr];
|
|
} else {
|
|
return L1IcacheMemory[addr];
|
|
}
|
|
}
|
|
|
|
void changeL1Permission(Address addr, AccessPermission permission) {
|
|
if (L1DcacheMemory.isTagPresent(addr)) {
|
|
return L1DcacheMemory.changePermission(addr, permission);
|
|
} else if(L1IcacheMemory.isTagPresent(addr)) {
|
|
return L1IcacheMemory.changePermission(addr, permission);
|
|
} else {
|
|
error("cannot change permission, L1 block not present");
|
|
}
|
|
}
|
|
|
|
bool isL1CacheTagPresent(Address addr) {
|
|
return (L1DcacheMemory.isTagPresent(addr) || L1IcacheMemory.isTagPresent(addr));
|
|
}
|
|
|
|
State getState(Address addr) {
|
|
if((L1DcacheMemory.isTagPresent(addr) && L1IcacheMemory.isTagPresent(addr)) == true){
|
|
DEBUG_EXPR(id);
|
|
DEBUG_EXPR(addr);
|
|
}
|
|
assert((L1DcacheMemory.isTagPresent(addr) && L1IcacheMemory.isTagPresent(addr)) == false);
|
|
|
|
if(L1_TBEs.isPresent(addr)) {
|
|
return L1_TBEs[addr].TBEState;
|
|
} else if (isL1CacheTagPresent(addr)) {
|
|
return getL1CacheEntry(addr).CacheState;
|
|
}
|
|
return State:NP;
|
|
}
|
|
|
|
string getStateStr(Address addr) {
|
|
return L1Cache_State_to_string(getState(addr));
|
|
}
|
|
|
|
// when is this called?
|
|
void setState(Address addr, State state) {
|
|
assert((L1DcacheMemory.isTagPresent(addr) && L1IcacheMemory.isTagPresent(addr)) == false);
|
|
|
|
// MUST CHANGE
|
|
if(L1_TBEs.isPresent(addr)) {
|
|
L1_TBEs[addr].TBEState := state;
|
|
}
|
|
|
|
if (isL1CacheTagPresent(addr)) {
|
|
getL1CacheEntry(addr).CacheState := state;
|
|
|
|
// Set permission
|
|
if (state == State:L1_I || state == State:L1_SI || state == State:L1_MI) {
|
|
changeL1Permission(addr, AccessPermission:Invalid);
|
|
} else if (state == State:L1_S) {
|
|
changeL1Permission(addr, AccessPermission:Read_Only);
|
|
} else if (state == State:L1_M) {
|
|
changeL1Permission(addr, AccessPermission:Read_Write);
|
|
} else {
|
|
changeL1Permission(addr, AccessPermission:Busy);
|
|
}
|
|
}
|
|
}
|
|
|
|
Event mandatory_request_type_to_event(CacheRequestType type) {
|
|
if (type == CacheRequestType:LD) {
|
|
return Event:Load;
|
|
} else if (type == CacheRequestType:IFETCH) {
|
|
return Event:Ifetch;
|
|
} else if ((type == CacheRequestType:ST) || (type == CacheRequestType:ATOMIC)) {
|
|
return Event:Store;
|
|
} else {
|
|
error("Invalid CacheRequestType");
|
|
}
|
|
}
|
|
|
|
// ** OUT_PORTS **
|
|
// All ports are to the same CMP network, queue id numbers determine IntraChip Switch location
|
|
|
|
out_port(requestIntraChipL1Network_out, RequestMsg, requestFromL1Cache);
|
|
out_port(responseIntraChipL1Network_out, ResponseMsg, responseFromL1Cache);
|
|
|
|
// ** IN_PORTS **
|
|
in_port(dummyTo0_in, RequestMsg, dummyTo0) {
|
|
if (dummyTo0_in.isReady()) {
|
|
peek(dummyTo0_in, RequestMsg) {
|
|
DEBUG_EXPR(in_msg.Address);
|
|
DEBUG_EXPR(machineID);
|
|
DEBUG_EXPR(in_msg.Type);
|
|
DEBUG_EXPR(getState(in_msg.Address));
|
|
DEBUG_EXPR(in_msg.RequestorMachId);
|
|
}
|
|
error("dummyTo0 port should not be used");
|
|
}
|
|
}
|
|
|
|
in_port(dummyTo1_in, RequestMsg, dummyTo1) {
|
|
if (dummyTo1_in.isReady()) {
|
|
peek(dummyTo1_in, RequestMsg) {
|
|
DEBUG_EXPR(in_msg.Address);
|
|
DEBUG_EXPR(machineID);
|
|
DEBUG_EXPR(in_msg.Type);
|
|
DEBUG_EXPR(getState(in_msg.Address));
|
|
DEBUG_EXPR(in_msg.RequestorMachId);
|
|
}
|
|
error("dummyTo1 port should not be used");
|
|
}
|
|
}
|
|
|
|
in_port(dummyTo4_in, ResponseMsg, dummyTo4) {
|
|
if (dummyTo4_in.isReady()) {
|
|
peek(dummyTo4_in, ResponseMsg) {
|
|
DEBUG_EXPR(in_msg.Address);
|
|
DEBUG_EXPR(machineID);
|
|
DEBUG_EXPR(in_msg.Type);
|
|
DEBUG_EXPR(getState(in_msg.Address));
|
|
DEBUG_EXPR(in_msg.SenderMachId);
|
|
}
|
|
error("dummyTo4 port should not be used");
|
|
}
|
|
}
|
|
|
|
// Response IntraChip L1 Network - response msg to this L1 cache
|
|
in_port(responseIntraChipL1Network_in, ResponseMsg, responseToL1Cache) {
|
|
if (responseIntraChipL1Network_in.isReady()) {
|
|
peek(responseIntraChipL1Network_in, ResponseMsg) {
|
|
DEBUG_EXPR(in_msg.Address);
|
|
DEBUG_EXPR(in_msg.Destination);
|
|
DEBUG_EXPR(in_msg.SenderMachId);
|
|
DEBUG_EXPR(machineID);
|
|
assert(in_msg.Destination.isElement(machineID));
|
|
if(machineIDToMachineType(in_msg.SenderMachId) == MachineType:L2Cache) {
|
|
if(in_msg.Type == CoherenceResponseType:DATA) {
|
|
trigger(Event:L1_Data, in_msg.Address); // L1 now has data in its desired state
|
|
} else if(in_msg.Type == CoherenceResponseType:DATA_S) {
|
|
trigger(Event:L1_Data_S, in_msg.Address); // L1 now has data but must imediately move to S state
|
|
} else if(in_msg.Type == CoherenceResponseType:DATA_I) {
|
|
trigger(Event:L1_Data_I, in_msg.Address); // L1 now has data but must imediately move to INV state
|
|
} else if(in_msg.Type == CoherenceResponseType:ACK) {
|
|
trigger(Event:L1_PutAck, in_msg.Address);
|
|
} else {
|
|
error("Invalid L1 response type");
|
|
}
|
|
} else {
|
|
error("A non-L2 cache sent a response to a L1 cache");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Request InterChip network - request from this L1 cache to the shared L2
|
|
in_port(requestIntraChipL1Network_in, RequestMsg, requestToL1Cache) {
|
|
if(requestIntraChipL1Network_in.isReady()) {
|
|
peek(requestIntraChipL1Network_in, RequestMsg) {
|
|
assert(in_msg.Destination.isElement(machineID));
|
|
if(machineIDToMachineType(in_msg.RequestorMachId) == MachineType:L2Cache) {
|
|
if(in_msg.Type == CoherenceRequestType:L1_DG) {
|
|
trigger(Event:L1_DownGrade, in_msg.Address); // Force L1 to downgrade to S state
|
|
} else if (in_msg.Type == CoherenceRequestType:INV) {
|
|
trigger(Event:L1_INV, in_msg.Address); // L1 must invalidate it's modified version
|
|
} else if (in_msg.Type == CoherenceRequestType:INV_S) {
|
|
trigger(Event:L1_INV_S, in_msg.Address); // L1 must invalidate it's shared version
|
|
} else {
|
|
error("Invalid forwarded request type");
|
|
}
|
|
} else {
|
|
error("A non-L2 cache sent a request to a L1 cache");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Mandatory Queue betweens Node's CPU and it's L1 caches
|
|
in_port(mandatoryQueue_in, CacheMsg, mandatoryQueue, desc="...") {
|
|
if (mandatoryQueue_in.isReady()) {
|
|
peek(mandatoryQueue_in, CacheMsg) {
|
|
|
|
// Check for data access to blocks in I-cache and ifetchs to blocks in D-cache
|
|
|
|
if (in_msg.Type == CacheRequestType:IFETCH) {
|
|
// ** INSTRUCTION ACCESS ***
|
|
|
|
// Check to see if it is in the OTHER L1
|
|
if (L1DcacheMemory.isTagPresent(in_msg.Address)) {
|
|
// The block is in the wrong L1, put the request on the queue to the shared L2
|
|
trigger(Event:L1_WriteBack, in_msg.Address);
|
|
}
|
|
if (L1IcacheMemory.isTagPresent(in_msg.Address)) {
|
|
// The tag matches for the L1, so the L1 asks the L2 for it.
|
|
trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address);
|
|
} else {
|
|
if (L1IcacheMemory.cacheAvail(in_msg.Address)) {
|
|
// L1 does't have the line, but we have space for it in the L1 so let's see if the L2 has it
|
|
trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address);
|
|
} else {
|
|
// No room in the L1, so we need to make room in the L1
|
|
trigger(Event:L1_Replacement, L1IcacheMemory.cacheProbe(in_msg.Address));
|
|
}
|
|
}
|
|
} else {
|
|
// *** DATA ACCESS ***
|
|
|
|
// Check to see if it is in the OTHER L1
|
|
if (L1IcacheMemory.isTagPresent(in_msg.Address)) {
|
|
// The block is in the wrong L1, put the request on the queue to the shared L2
|
|
trigger(Event:L1_WriteBack, in_msg.Address);
|
|
}
|
|
if (L1DcacheMemory.isTagPresent(in_msg.Address)) {
|
|
// The tag matches for the L1, so the L1 ask the L2 for it
|
|
trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address);
|
|
} else {
|
|
if (L1DcacheMemory.cacheAvail(in_msg.Address)) {
|
|
// L1 does't have the line, but we have space for it in the L1 let's see if the L2 has it
|
|
trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address);
|
|
} else {
|
|
// No room in the L1, so we need to make room in the L1
|
|
trigger(Event:L1_Replacement, L1DcacheMemory.cacheProbe(in_msg.Address));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ACTIONS
|
|
action(a_issueGETS, "a", desc="Issue GETS") {
|
|
peek(mandatoryQueue_in, CacheMsg) {
|
|
enqueue(requestIntraChipL1Network_out, RequestMsg, latency="L1_REQUEST_LATENCY") {
|
|
out_msg.Address := address;
|
|
out_msg.Type := CoherenceRequestType:GETS;
|
|
out_msg.RequestorMachId := machineID;
|
|
out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID));
|
|
DEBUG_EXPR(address);
|
|
DEBUG_EXPR(out_msg.Destination);
|
|
out_msg.MessageSize := MessageSizeType:Control;
|
|
out_msg.L1CacheStateStr := getStateStr(address);
|
|
out_msg.Prefetch := in_msg.Prefetch;
|
|
out_msg.AccessMode := in_msg.AccessMode;
|
|
}
|
|
}
|
|
}
|
|
|
|
action(b_issueGETX, "b", desc="Issue GETX") {
|
|
peek(mandatoryQueue_in, CacheMsg) {
|
|
enqueue(requestIntraChipL1Network_out, RequestMsg, latency="L1_REQUEST_LATENCY") {
|
|
out_msg.Address := address;
|
|
out_msg.Type := CoherenceRequestType:GETX;
|
|
out_msg.RequestorMachId := machineID;
|
|
DEBUG_EXPR(machineID);
|
|
out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID));
|
|
DEBUG_EXPR(address);
|
|
DEBUG_EXPR(out_msg.Destination);
|
|
out_msg.MessageSize := MessageSizeType:Control;
|
|
out_msg.L1CacheStateStr := getStateStr(address);
|
|
out_msg.Prefetch := in_msg.Prefetch;
|
|
out_msg.AccessMode := in_msg.AccessMode;
|
|
}
|
|
}
|
|
}
|
|
|
|
action(c_issueUPGRADE, "c", desc="Issue GETX") {
|
|
peek(mandatoryQueue_in, CacheMsg) {
|
|
enqueue(requestIntraChipL1Network_out, RequestMsg, latency="L1_REQUEST_LATENCY") {
|
|
out_msg.Address := address;
|
|
out_msg.Type := CoherenceRequestType:UPGRADE;
|
|
out_msg.RequestorMachId := machineID;
|
|
out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID));
|
|
DEBUG_EXPR(address);
|
|
DEBUG_EXPR(out_msg.Destination);
|
|
out_msg.MessageSize := MessageSizeType:Control;
|
|
out_msg.L1CacheStateStr := getStateStr(address);
|
|
out_msg.Prefetch := in_msg.Prefetch;
|
|
out_msg.AccessMode := in_msg.AccessMode;
|
|
}
|
|
}
|
|
}
|
|
|
|
action(f_issueGETINSTR, "g", desc="Issue GETINSTR") {
|
|
peek(mandatoryQueue_in, CacheMsg) {
|
|
enqueue(requestIntraChipL1Network_out, RequestMsg, latency="L1_REQUEST_LATENCY") {
|
|
out_msg.Address := address;
|
|
out_msg.Type := CoherenceRequestType:GET_INSTR;
|
|
out_msg.RequestorMachId := machineID;
|
|
out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID));
|
|
DEBUG_EXPR(address);
|
|
DEBUG_EXPR(out_msg.Destination);
|
|
out_msg.MessageSize := MessageSizeType:Control;
|
|
out_msg.L1CacheStateStr := getStateStr(address);
|
|
out_msg.Prefetch := in_msg.Prefetch;
|
|
out_msg.AccessMode := in_msg.AccessMode;
|
|
}
|
|
}
|
|
}
|
|
|
|
action(d_issuePUTX, "d", desc="Issue PUTX") {
|
|
enqueue(requestIntraChipL1Network_out, RequestMsg, latency="L1_REQUEST_LATENCY") {
|
|
out_msg.Address := address;
|
|
out_msg.Type := CoherenceRequestType:PUTX;
|
|
out_msg.RequestorMachId := machineID;
|
|
out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID));
|
|
out_msg.DataBlk := getL1CacheEntry(address).DataBlk;
|
|
DEBUG_EXPR(address);
|
|
DEBUG_EXPR(out_msg.Destination);
|
|
DEBUG_EXPR(out_msg.DataBlk);
|
|
out_msg.MessageSize := MessageSizeType:Data;
|
|
out_msg.L1CacheStateStr := getStateStr(address);
|
|
}
|
|
}
|
|
|
|
action(q_issuePUTS, "q", desc="Issue PUTS") {
|
|
enqueue(requestIntraChipL1Network_out, RequestMsg, latency="L1_REQUEST_LATENCY") {
|
|
out_msg.Address := address;
|
|
out_msg.Type := CoherenceRequestType:PUTS;
|
|
out_msg.RequestorMachId := machineID;
|
|
out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID));
|
|
DEBUG_EXPR(address);
|
|
DEBUG_EXPR(out_msg.Destination);
|
|
out_msg.DataBlk := getL1CacheEntry(address).DataBlk;
|
|
out_msg.MessageSize := MessageSizeType:Data;
|
|
out_msg.L1CacheStateStr := getStateStr(address);
|
|
}
|
|
}
|
|
|
|
// L1 responding to a L2 request with data
|
|
action(e_dataFromL1CacheToL2Cache, "e", desc="Send data from L1 cache to L2 Cache") {
|
|
enqueue(responseIntraChipL1Network_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") {
|
|
out_msg.Address := address;
|
|
out_msg.Type := CoherenceResponseType:DATA;
|
|
out_msg.SenderMachId := machineID;
|
|
out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID));
|
|
out_msg.DataBlk := getL1CacheEntry(address).DataBlk;
|
|
DEBUG_EXPR(address);
|
|
DEBUG_EXPR(out_msg.Destination);
|
|
DEBUG_EXPR(out_msg.DataBlk);
|
|
out_msg.MessageSize := MessageSizeType:Data;
|
|
}
|
|
}
|
|
|
|
action(f_dataFromTBEToL2Cache, "f", desc="Send data from L1_TBE to L2 Cache") {
|
|
peek(requestIntraChipL1Network_in, RequestMsg) {
|
|
enqueue(responseIntraChipL1Network_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") {
|
|
out_msg.Address := address;
|
|
out_msg.Type := CoherenceResponseType:DATA;
|
|
out_msg.SenderMachId := machineID;
|
|
out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID));
|
|
out_msg.DataBlk := L1_TBEs[in_msg.Address].DataBlk;
|
|
DEBUG_EXPR(address);
|
|
DEBUG_EXPR(out_msg.Destination);
|
|
DEBUG_EXPR(out_msg.DataBlk);
|
|
out_msg.MessageSize := MessageSizeType:Data;
|
|
}
|
|
}
|
|
}
|
|
|
|
// L1 responding to a L2 request with an invadiation ack
|
|
action(t_sendInvAckToL2Cache, "t", desc="Send Invadiation ack to L2 Cache") {
|
|
enqueue(responseIntraChipL1Network_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") {
|
|
out_msg.Address := address;
|
|
out_msg.Type := CoherenceResponseType:INV_ACK;
|
|
out_msg.SenderMachId := machineID;
|
|
out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID));
|
|
DEBUG_EXPR(address);
|
|
DEBUG_EXPR(out_msg.Destination);
|
|
out_msg.MessageSize := MessageSizeType:Control;
|
|
}
|
|
}
|
|
|
|
action(h_load_hit, "h", desc="If not prefetch, notify sequencer the load completed.") {
|
|
DEBUG_EXPR(getL1CacheEntry(address).DataBlk);
|
|
sequencer.readCallback(address, getL1CacheEntry(address).DataBlk);
|
|
}
|
|
|
|
action(hh_store_hit, "\h", desc="If not prefetch, notify sequencer that store completed.") {
|
|
DEBUG_EXPR(getL1CacheEntry(address).DataBlk);
|
|
sequencer.writeCallback(address, getL1CacheEntry(address).DataBlk);
|
|
}
|
|
|
|
action(i_allocateTBE, "i", desc="Allocate TBE (isPrefetch=0, number of invalidates=0)") {
|
|
check_allocate(L1_TBEs);
|
|
L1_TBEs.allocate(address);
|
|
L1_TBEs[address].isPrefetch := false;
|
|
}
|
|
|
|
action(k_popMandatoryQueue, "k", desc="Pop mandatory queue.") {
|
|
mandatoryQueue_in.dequeue();
|
|
}
|
|
|
|
action(l_popRequestQueue, "l", desc="Pop incoming request queue and profile the delay within this virtual network") {
|
|
profileMsgDelay(2, requestIntraChipL1Network_in.dequeue_getDelayCycles());
|
|
}
|
|
|
|
action(o_popIncomingResponseQueue, "o", desc="Pop Incoming Response queue and profile the delay within this virtual network") {
|
|
profileMsgDelay(3, responseIntraChipL1Network_in.dequeue_getDelayCycles());
|
|
}
|
|
|
|
action(s_deallocateTBE, "s", desc="Deallocate TBE") {
|
|
L1_TBEs.deallocate(address);
|
|
}
|
|
|
|
action(u_writeDataToL1Cache, "u", desc="Write data to cache") {
|
|
peek(responseIntraChipL1Network_in, ResponseMsg) {
|
|
getL1CacheEntry(address).DataBlk := in_msg.DataBlk;
|
|
}
|
|
}
|
|
|
|
action(x_copyDataFromL1CacheToTBE, "x", desc="Copy data from cache to TBE") {
|
|
L1_TBEs[address].DataBlk := getL1CacheEntry(address).DataBlk;
|
|
}
|
|
|
|
action(z_stall, "z", desc="Stall") {
|
|
}
|
|
|
|
action(ff_deallocateL1CacheBlock, "\f", desc="Deallocate L1 cache block. Sets the cache to not present, allowing a replacement in parallel with a fetch.") {
|
|
if (L1DcacheMemory.isTagPresent(address)) {
|
|
L1DcacheMemory.deallocate(address);
|
|
} else {
|
|
L1IcacheMemory.deallocate(address);
|
|
}
|
|
}
|
|
|
|
action(oo_allocateL1DCacheBlock, "\o", desc="Set L1 D-cache tag equal to tag of block B.") {
|
|
if (L1DcacheMemory.isTagPresent(address) == false) {
|
|
L1DcacheMemory.allocate(address);
|
|
}
|
|
}
|
|
|
|
action(pp_allocateL1ICacheBlock, "\p", desc="Set L1 I-cache tag equal to tag of block B.") {
|
|
if (L1IcacheMemory.isTagPresent(address) == false) {
|
|
L1IcacheMemory.allocate(address);
|
|
}
|
|
}
|
|
|
|
//*****************************************************
|
|
// TRANSITIONS
|
|
//*****************************************************
|
|
|
|
// Transitions for Load/Store/Replacement/WriteBack from transient states
|
|
transition({L1_IS, L1_IM, L1_ISI, L1_IMI, L1_IMS, L1_IMSI, L1_SI, L1_MI}, {Load, Ifetch, Store, L1_Replacement, L1_WriteBack}) {
|
|
z_stall;
|
|
}
|
|
|
|
// Transitions from Idle
|
|
transition({NP,L1_I}, {L1_Replacement, L1_WriteBack}) {
|
|
ff_deallocateL1CacheBlock;
|
|
}
|
|
|
|
transition({NP,L1_I}, Load, L1_IS) {
|
|
oo_allocateL1DCacheBlock;
|
|
i_allocateTBE;
|
|
a_issueGETS;
|
|
k_popMandatoryQueue;
|
|
}
|
|
|
|
transition({NP,L1_I}, Ifetch, L1_IS) {
|
|
pp_allocateL1ICacheBlock;
|
|
i_allocateTBE;
|
|
f_issueGETINSTR;
|
|
k_popMandatoryQueue;
|
|
}
|
|
|
|
transition({NP,L1_I}, Store, L1_IM) {
|
|
oo_allocateL1DCacheBlock;
|
|
i_allocateTBE;
|
|
b_issueGETX;
|
|
k_popMandatoryQueue;
|
|
}
|
|
|
|
// Transitions from Shared
|
|
transition({L1_S}, {Load,Ifetch}) {
|
|
h_load_hit;
|
|
k_popMandatoryQueue;
|
|
}
|
|
|
|
transition(L1_S, Store, L1_IM) {
|
|
i_allocateTBE;
|
|
c_issueUPGRADE;
|
|
k_popMandatoryQueue;
|
|
}
|
|
|
|
transition(L1_S, {L1_Replacement,L1_WriteBack}, L1_SI) {
|
|
i_allocateTBE;
|
|
q_issuePUTS;
|
|
x_copyDataFromL1CacheToTBE;
|
|
ff_deallocateL1CacheBlock;
|
|
}
|
|
|
|
transition(L1_S, L1_INV_S, L1_I) {
|
|
t_sendInvAckToL2Cache;
|
|
l_popRequestQueue;
|
|
}
|
|
|
|
// Transitions from Modified
|
|
transition(L1_M, {Load, Ifetch}) {
|
|
h_load_hit;
|
|
k_popMandatoryQueue;
|
|
}
|
|
|
|
transition(L1_M, Store) {
|
|
hh_store_hit;
|
|
k_popMandatoryQueue;
|
|
}
|
|
|
|
transition(L1_M, {L1_Replacement, L1_WriteBack}, L1_MI) {
|
|
i_allocateTBE;
|
|
d_issuePUTX;
|
|
x_copyDataFromL1CacheToTBE;
|
|
ff_deallocateL1CacheBlock;
|
|
}
|
|
|
|
transition(L1_M, L1_INV, L1_I) {
|
|
e_dataFromL1CacheToL2Cache;
|
|
l_popRequestQueue;
|
|
}
|
|
|
|
transition(L1_M, L1_DownGrade, L1_S) {
|
|
e_dataFromL1CacheToL2Cache;
|
|
l_popRequestQueue;
|
|
}
|
|
|
|
// Transitions from L1_IS
|
|
transition(L1_IS, L1_INV_S, L1_ISI) {
|
|
t_sendInvAckToL2Cache;
|
|
l_popRequestQueue;
|
|
}
|
|
|
|
transition(L1_IS, L1_Data, L1_S) {
|
|
u_writeDataToL1Cache;
|
|
h_load_hit;
|
|
s_deallocateTBE;
|
|
o_popIncomingResponseQueue;
|
|
}
|
|
|
|
transition(L1_IS, L1_Data_I, L1_I) {
|
|
u_writeDataToL1Cache;
|
|
h_load_hit;
|
|
s_deallocateTBE;
|
|
o_popIncomingResponseQueue;
|
|
}
|
|
|
|
// Transitions from L1_ISI
|
|
transition(L1_ISI, L1_Data, L1_I) {
|
|
u_writeDataToL1Cache;
|
|
h_load_hit;
|
|
s_deallocateTBE;
|
|
o_popIncomingResponseQueue;
|
|
}
|
|
|
|
// Transitions from L1_IM
|
|
transition(L1_IM, L1_INV, L1_IMI) { // we don't have to respond immediately because we know the data is coming
|
|
l_popRequestQueue;
|
|
}
|
|
|
|
transition(L1_IM, L1_INV_S) {
|
|
t_sendInvAckToL2Cache;
|
|
l_popRequestQueue;
|
|
}
|
|
|
|
transition(L1_IM, L1_DownGrade, L1_IMS) {
|
|
l_popRequestQueue;
|
|
}
|
|
|
|
transition(L1_IM, L1_Data, L1_M) {
|
|
u_writeDataToL1Cache;
|
|
hh_store_hit;
|
|
s_deallocateTBE;
|
|
o_popIncomingResponseQueue;
|
|
}
|
|
|
|
transition(L1_IM, L1_Data_S, L1_S) {
|
|
u_writeDataToL1Cache;
|
|
hh_store_hit;
|
|
s_deallocateTBE;
|
|
e_dataFromL1CacheToL2Cache;
|
|
o_popIncomingResponseQueue;
|
|
}
|
|
|
|
transition(L1_IM, L1_Data_I, L1_I) {
|
|
u_writeDataToL1Cache;
|
|
hh_store_hit;
|
|
s_deallocateTBE;
|
|
e_dataFromL1CacheToL2Cache;
|
|
o_popIncomingResponseQueue;
|
|
}
|
|
|
|
// Transitions from L1_IMI - data should arrive and no request are possilbe
|
|
transition(L1_IMI, L1_Data, L1_I) {
|
|
u_writeDataToL1Cache;
|
|
hh_store_hit;
|
|
s_deallocateTBE;
|
|
e_dataFromL1CacheToL2Cache;
|
|
o_popIncomingResponseQueue;
|
|
}
|
|
|
|
// Transitions from L1_IMS
|
|
transition(L1_IMS, L1_Data, L1_S) {
|
|
u_writeDataToL1Cache;
|
|
hh_store_hit;
|
|
s_deallocateTBE;
|
|
e_dataFromL1CacheToL2Cache;
|
|
o_popIncomingResponseQueue;
|
|
}
|
|
|
|
transition(L1_IMS, L1_INV_S, L1_IMSI) {
|
|
l_popRequestQueue;
|
|
}
|
|
|
|
// Transitions from L1_IMSI
|
|
transition(L1_IMSI, L1_Data, L1_I) {
|
|
u_writeDataToL1Cache;
|
|
hh_store_hit;
|
|
s_deallocateTBE;
|
|
e_dataFromL1CacheToL2Cache;
|
|
o_popIncomingResponseQueue;
|
|
}
|
|
|
|
// Transitions from L1_SI
|
|
transition(L1_SI, L1_INV_S) {
|
|
t_sendInvAckToL2Cache;
|
|
l_popRequestQueue;
|
|
}
|
|
|
|
transition(L1_SI, L1_PutAck, L1_I) {
|
|
s_deallocateTBE;
|
|
o_popIncomingResponseQueue;
|
|
}
|
|
|
|
// Transitions from L1_MI
|
|
transition(L1_MI, L1_INV) {
|
|
f_dataFromTBEToL2Cache;
|
|
l_popRequestQueue;
|
|
}
|
|
|
|
transition(L1_MI, L1_DownGrade, L1_SI) {
|
|
f_dataFromTBEToL2Cache;
|
|
l_popRequestQueue;
|
|
}
|
|
|
|
transition(L1_MI, L1_PutAck, L1_I) {
|
|
s_deallocateTBE;
|
|
o_popIncomingResponseQueue;
|
|
}
|
|
}
|
|
|
|
|
|
|