blob: 355027ad072f86598bf4e4b2223cec41c8d6d31f [file] [log] [blame]
/*
* Copyright (c) 2021 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
* not be construed as granting a license to any other intellectual
* property including but not limited to intellectual property relating
* to a hardware implementation of the functionality of the software
* licensed hereunder. You may use the software subject to the license
* terms below provided that you ensure that this notice is replicated
* unmodified and in its entirety in all distributions of the software,
* modified or unmodified, in source code or in binary form.
*
* 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:Memory, "Memory controller interface") :
// no explicit modeling of allocation latency like the Caches, so add one
// cycle to the response enqueue latency as default
Cycles response_latency := 2;
Cycles data_latency := 1;
Cycles to_memory_controller_latency := 1;
int data_channel_size;
// Interface to the network
// Note vnet_type is used by Garnet only. "response" type is assumed to
// have data, so use it for data channels and "none" for the rest.
// network="To" for outbound queue; network="From" for inbound
// virtual networks: 0=request, 1=snoop, 2=response, 3=data
MessageBuffer * reqOut, network="To", virtual_network="0", vnet_type="none";
MessageBuffer * snpOut, network="To", virtual_network="1", vnet_type="none";
MessageBuffer * rspOut, network="To", virtual_network="2", vnet_type="none";
MessageBuffer * datOut, network="To", virtual_network="3", vnet_type="response";
MessageBuffer * reqIn, network="From", virtual_network="0", vnet_type="none";
MessageBuffer * snpIn, network="From", virtual_network="1", vnet_type="none";
MessageBuffer * rspIn, network="From", virtual_network="2", vnet_type="none";
MessageBuffer * datIn, network="From", virtual_network="3", vnet_type="response";
// Requests that can allocate a TBE
MessageBuffer * reqRdy;
// Data/ack to/from memory
MessageBuffer * requestToMemory;
MessageBuffer * responseFromMemory;
// Trigger queue for internal events
MessageBuffer * triggerQueue;
{
////////////////////////////////////////////////////////////////////////////
// States
////////////////////////////////////////////////////////////////////////////
state_declaration(State, desc="Transaction states", default="Memory_State_READY") {
// We don't know if the line is cached, so the memory copy is maybe stable
READY, AccessPermission:Backing_Store, desc="Ready to transfer the line";
WAITING_NET_DATA, AccessPermission:Backing_Store_Busy, desc="Waiting data from the network";
SENDING_NET_DATA, AccessPermission:Backing_Store_Busy, desc="Sending data to the network";
READING_MEM, AccessPermission:Backing_Store_Busy, desc="Waiting data from memory";
// Null state for debugging; allow writes
null, AccessPermission:Backing_Store, desc="Null state";
}
////////////////////////////////////////////////////////////////////////////
// Events
////////////////////////////////////////////////////////////////////////////
enumeration(Event, desc="Memory events") {
// Checks if a request can allocate a TBE be moved to reqRdy
CheckAllocTBE, desc="";
CheckAllocTBE_WithCredit, desc="";
// Requests
WriteNoSnpPtl, desc="";
WriteNoSnp, desc="";
ReadNoSnp, desc="";
ReadNoSnpSep, desc="";
// Data
WriteData, desc="";
// Memory side
MemoryData, desc="";
MemoryAck, desc="";
// Internal event triggers
Trigger_Send, desc="";
Trigger_SendDone, desc="";
Trigger_ReceiveDone, desc="";
Trigger_SendRetry, desc="";
Trigger_SendPCrdGrant, desc="";
}
// Is there a less tedious way to convert messages to events ??
Event reqToEvent (CHIRequestType type) {
if (type == CHIRequestType:WriteNoSnpPtl) {
return Event:WriteNoSnpPtl;
} else if (type == CHIRequestType:WriteNoSnp) {
return Event:WriteNoSnp;
} else if (type == CHIRequestType:ReadNoSnp) {
return Event:ReadNoSnp;
} else if (type == CHIRequestType:ReadNoSnpSep) {
return Event:ReadNoSnpSep;
} else {
error("Invalid CHIRequestType");
}
}
Event respToEvent (CHIResponseType type) {
error("Invalid CHIResponseType");
}
Event dataToEvent (CHIDataType type) {
if (type == CHIDataType:NCBWrData) {
return Event:WriteData;
} else {
error("Invalid CHIDataType");
}
}
////////////////////////////////////////////////////////////////////////////
// Data structures
////////////////////////////////////////////////////////////////////////////
// Cache block size
int blockSize, default="RubySystem::getBlockSizeBytes()";
// TBE fields
structure(TBE, desc="...") {
int storSlot, desc="Slot in the storage tracker occupied by this entry";
Addr addr, desc="Line address for this TBE";
Addr accAddr, desc="Original access address. Set only for Write*Ptl";
int accSize, desc="Access size. Set only for Write*Ptl";
State state, desc="Current line state";
DataBlock dataBlk, desc="Transaction data";
WriteMask dataBlkValid, desc="valid bytes in dataBlk";
int rxtxBytes, desc="Bytes sent or received";
MachineID requestor, desc="Requestor that originated this request";
MachineID destination, desc="Where we are sending data";
bool useDataSepResp, desc="Replies with DataSepResp instead of CompData";
}
structure(TBETable, external ="yes") {
TBE lookup(Addr);
void allocate(Addr);
void deallocate(Addr);
bool isPresent(Addr);
bool areNSlotsAvailable(int n, Tick curTime);
}
structure(TBEStorage, external ="yes") {
int size();
int capacity();
int reserved();
int slotsAvailable();
bool areNSlotsAvailable(int n);
void incrementReserved();
void decrementReserved();
int addEntryToNewSlot();
void removeEntryFromSlot(int slot);
}
TBETable TBEs, template="<Memory_TBE>", constructor="m_number_of_TBEs";
TBEStorage storTBEs, constructor="this, m_number_of_TBEs";
// Tracks all pending MemoryAcks (debug purposes only)
int pendingWrites, default="0";
structure(TriggerMsg, desc="...", interface="Message") {
Addr addr;
Event event;
MachineID retryDest;
bool functionalRead(Packet *pkt) { return false; }
bool functionalRead(Packet *pkt, WriteMask &mask) { return false; }
bool functionalWrite(Packet *pkt) { return false; }
}
// Tracks a pending credit request from a retry
structure(RetryQueueEntry) {
Addr addr, desc="Line address";
MachineID retryDest, desc="Retry destination";
}
structure(TriggerQueue, external ="yes") {
void pop();
bool empty();
void emplace(Addr,MachineID);
RetryQueueEntry next();
}
TriggerQueue retryQueue, template="<Memory_RetryQueueEntry>";
////////////////////////////////////////////////////////////////////////////
// External functions
////////////////////////////////////////////////////////////////////////////
Tick clockEdge();
Tick curTick();
Tick cyclesToTicks(Cycles c);
void set_tbe(TBE b);
void unset_tbe();
void wakeUpAllBuffers(Addr a);
bool respondsTo(Addr addr);
////////////////////////////////////////////////////////////////////////////
// Interface functions required by SLICC
////////////////////////////////////////////////////////////////////////////
State getState(TBE tbe, Addr addr) {
if (is_valid(tbe)) {
assert(tbe.addr == addr);
return tbe.state;
} else {
return State:READY;
}
}
void setState(TBE tbe, Addr addr, State state) {
if (is_valid(tbe)) {
assert(tbe.addr == addr);
tbe.state := state;
}
}
AccessPermission getAccessPermission(Addr addr) {
if (respondsTo(addr)) {
TBE tbe := TBEs[addr];
if (is_valid(tbe)) {
DPRINTF(RubySlicc, "%x %s,%s\n", addr, tbe.state, Memory_State_to_permission(tbe.state));
return Memory_State_to_permission(tbe.state);
} else {
DPRINTF(RubySlicc, "%x %s\n", addr, AccessPermission:Backing_Store);
return AccessPermission:Backing_Store;
}
} else {
DPRINTF(RubySlicc, "%x %s\n", addr, AccessPermission:NotPresent);
return AccessPermission:NotPresent;
}
}
void setAccessPermission(Addr addr, State state) {
}
void functionalRead(Addr addr, Packet *pkt, WriteMask &mask) {
if (respondsTo(addr)) {
DPRINTF(RubySlicc, "functionalRead %x\n", addr);
TBE tbe := TBEs[addr];
if (mask.isEmpty()) {
functionalMemoryRead(pkt);
mask.fillMask();
DPRINTF(RubySlicc, "functionalRead mem %x %s\n", addr, mask);
}
// Update with any transient data
//TODO additional handling of partial data ??
if (is_valid(tbe)) {
WriteMask read_mask;
read_mask.setMask(addressOffset(tbe.accAddr, tbe.addr), tbe.accSize);
read_mask.andMask(tbe.dataBlkValid);
if (read_mask.isEmpty() == false) {
testAndReadMask(addr, tbe.dataBlk, read_mask, pkt);
DPRINTF(RubySlicc, "functionalRead tbe %x %s %s %s\n", addr, tbe.dataBlk, read_mask, mask);
mask.orMask(read_mask);
}
}
}
}
int functionalWrite(Addr addr, Packet *pkt) {
if(respondsTo(addr)) {
int num_functional_writes := 0;
TBE tbe := TBEs[addr];
if (is_valid(tbe)) {
num_functional_writes := num_functional_writes +
testAndWrite(addr, tbe.dataBlk, pkt);
DPRINTF(RubySlicc, "functionalWrite tbe %x %s\n", addr, tbe.dataBlk);
}
num_functional_writes := num_functional_writes + functionalMemoryWrite(pkt);
DPRINTF(RubySlicc, "functionalWrite mem %x\n", addr);
return num_functional_writes;
} else {
return 0;
}
}
////////////////////////////////////////////////////////////////////////////
// Helper functions
////////////////////////////////////////////////////////////////////////////
void printResources() {
DPRINTF(RubySlicc, "Resources(avail/max): TBEs=%d/%d\n",
storTBEs.size(), storTBEs.capacity());
DPRINTF(RubySlicc, "Resources(in/out size): rdy=%d req=%d/%d rsp=%d/%d dat=%d/%d snp=%d/%d\n",
reqRdy.getSize(curTick()),
reqIn.getSize(curTick()), reqOut.getSize(curTick()),
rspIn.getSize(curTick()), rspOut.getSize(curTick()),
datIn.getSize(curTick()), datOut.getSize(curTick()),
snpIn.getSize(curTick()), snpOut.getSize(curTick()));
}
////////////////////////////////////////////////////////////////////////////
// Input/output port definitions
////////////////////////////////////////////////////////////////////////////
// Outbound port definitions
out_port(reqOutPort, CHIRequestMsg, reqOut);
out_port(snpOutPort, CHIRequestMsg, snpOut);
out_port(rspOutPort, CHIResponseMsg, rspOut);
out_port(datOutPort, CHIDataMsg, datOut);
out_port(triggerOutPort, TriggerMsg, triggerQueue);
out_port(memQueue_out, MemoryMsg, requestToMemory);
out_port(reqRdyOutPort, CHIRequestMsg, reqRdy);
// Inbound port definitions
// Response
in_port(rspInPort, CHIResponseMsg, rspIn, rank=6) {
if (rspInPort.isReady(clockEdge())) {
printResources();
peek(rspInPort, CHIResponseMsg) {
error("Unexpected message");
}
}
}
// Data
in_port(datInPort, CHIDataMsg, datIn, rank=5) {
if (datInPort.isReady(clockEdge())) {
printResources();
peek(datInPort, CHIDataMsg) {
int received := in_msg.bitMask.count();
assert((received <= data_channel_size) && (received > 0));
trigger(dataToEvent(in_msg.type), in_msg.addr, TBEs[in_msg.addr]);
}
}
}
// Data/Ack from memory
in_port(memQueue_in, MemoryMsg, responseFromMemory, rank=4) {
if (memQueue_in.isReady(clockEdge())) {
printResources();
peek(memQueue_in, MemoryMsg) {
Addr addr := makeLineAddress(in_msg.addr);
if (in_msg.Type == MemoryRequestType:MEMORY_READ) {
trigger(Event:MemoryData, addr, TBEs[addr]);
} else if (in_msg.Type == MemoryRequestType:MEMORY_WB) {
trigger(Event:MemoryAck, addr, TBEs[addr]);
} else {
error("Invalid message");
}
}
}
}
// Trigger
in_port(triggerInPort, TriggerMsg, triggerQueue, rank=3) {
if (triggerInPort.isReady(clockEdge())) {
printResources();
peek(triggerInPort, TriggerMsg) {
trigger(in_msg.event, in_msg.addr, TBEs[in_msg.addr]);
}
}
}
// Snoops
in_port(snpInPort, CHIRequestMsg, snpIn, rank=2) {
if (snpInPort.isReady(clockEdge())) {
printResources();
peek(snpInPort, CHIRequestMsg) {
error("Unexpected message");
}
}
}
// Requests
in_port(reqRdyInPort, CHIRequestMsg, reqRdy, rank=1) {
if (reqRdyInPort.isReady(clockEdge())) {
printResources();
peek(reqRdyInPort, CHIRequestMsg) {
trigger(reqToEvent(in_msg.type), in_msg.addr, TBEs[in_msg.addr]);
}
}
}
in_port(reqInPort, CHIRequestMsg, reqIn, rank=0) {
if (reqInPort.isReady(clockEdge())) {
printResources();
peek(reqInPort, CHIRequestMsg) {
if (in_msg.allowRetry) {
trigger(Event:CheckAllocTBE, in_msg.addr, TBEs[in_msg.addr]);
} else {
// Only expected requests that do not allow retry are the ones that
// are being retried after receiving credit
trigger(Event:CheckAllocTBE_WithCredit,
in_msg.addr, TBEs[in_msg.addr]);
}
}
}
}
////////////////////////////////////////////////////////////////////////////
// Actions
////////////////////////////////////////////////////////////////////////////
action(checkAllocateTBE, desc="") {
// Move to reqRdy if resources available, otherwise send retry
if (storTBEs.areNSlotsAvailable(1)) {
// reserve a slot for this request
storTBEs.incrementReserved();
peek(reqInPort, CHIRequestMsg) {
enqueue(reqRdyOutPort, CHIRequestMsg, 0) {
out_msg := in_msg;
}
}
} else {
peek(reqInPort, CHIRequestMsg) {
assert(in_msg.allowRetry);
enqueue(triggerOutPort, TriggerMsg, 0) {
out_msg.addr := in_msg.addr;
out_msg.event := Event:Trigger_SendRetry;
out_msg.retryDest := in_msg.requestor;
retryQueue.emplace(in_msg.addr,in_msg.requestor);
}
}
}
reqInPort.dequeue(clockEdge());
}
action(checkAllocateTBE_withCredit, desc="") {
// We must have reserved resources for this request
peek(reqInPort, CHIRequestMsg) {
assert(in_msg.allowRetry == false);
enqueue(reqRdyOutPort, CHIRequestMsg, 0) {
out_msg := in_msg;
}
}
reqInPort.dequeue(clockEdge());
}
action(allocateTBE, "atbe", desc="Allocate TBEs for a miss") {
// We must have reserved resources for this allocation
storTBEs.decrementReserved();
assert(storTBEs.areNSlotsAvailable(1));
TBEs.allocate(address);
set_tbe(TBEs[address]);
tbe.storSlot := storTBEs.addEntryToNewSlot();
tbe.addr := address;
tbe.rxtxBytes := 0;
tbe.useDataSepResp := false;
}
action(initializeFromReqTBE, "itbe", desc="Initialize TBE fields") {
peek(reqRdyInPort, CHIRequestMsg) {
tbe.requestor := in_msg.requestor;
if (in_msg.dataToFwdRequestor) {
tbe.destination := in_msg.fwdRequestor;
} else {
tbe.destination := in_msg.requestor;
}
tbe.accAddr := in_msg.accAddr;
tbe.accSize := in_msg.accSize;
}
}
action(decWritePending, "dwp", desc="Decrement pending writes") {
assert(pendingWrites >= 1);
pendingWrites := pendingWrites - 1;
}
action(deallocateTBE, "dtbe", desc="Deallocate TBEs") {
assert(is_valid(tbe));
storTBEs.removeEntryFromSlot(tbe.storSlot);
TBEs.deallocate(address);
unset_tbe();
// send credit if requestor waiting for it
if (retryQueue.empty() == false) {
assert(storTBEs.areNSlotsAvailable(1));
storTBEs.incrementReserved();
RetryQueueEntry e := retryQueue.next();
retryQueue.pop();
enqueue(triggerOutPort, TriggerMsg, 0) {
out_msg.addr := e.addr;
out_msg.retryDest := e.retryDest;
out_msg.event := Event:Trigger_SendPCrdGrant;
}
}
}
action(sendReadReceipt, "sRR", desc="Send receipt to requestor") {
assert(is_valid(tbe));
enqueue(rspOutPort, CHIResponseMsg, response_latency) {
out_msg.addr := address;
out_msg.type := CHIResponseType:ReadReceipt;
out_msg.responder := machineID;
out_msg.Destination.add(tbe.requestor);
}
// also send different type of data when ready
tbe.useDataSepResp := true;
}
action(sendCompDBIDResp, "sCbid", desc="Send ack to requestor") {
assert(is_valid(tbe));
enqueue(rspOutPort, CHIResponseMsg, response_latency) {
out_msg.addr := address;
out_msg.type := CHIResponseType:CompDBIDResp;
out_msg.responder := machineID;
out_msg.Destination.add(tbe.requestor);
}
}
action(sendMemoryRead, "smr", desc="Send request to memory") {
assert(is_valid(tbe));
enqueue(memQueue_out, MemoryMsg, to_memory_controller_latency) {
out_msg.addr := address;
out_msg.Type := MemoryRequestType:MEMORY_READ;
out_msg.Sender := tbe.requestor;
out_msg.MessageSize := MessageSizeType:Request_Control;
out_msg.Len := 0;
}
}
action(sendMemoryWrite, "smw", desc="Send request to memory") {
assert(is_valid(tbe));
enqueue(memQueue_out, MemoryMsg, to_memory_controller_latency) {
out_msg.addr := tbe.accAddr;
out_msg.Type := MemoryRequestType:MEMORY_WB;
out_msg.Sender := tbe.requestor;
out_msg.MessageSize := MessageSizeType:Writeback_Data;
out_msg.DataBlk := tbe.dataBlk;
out_msg.Len := tbe.accSize;
}
tbe.dataBlkValid.clear();
pendingWrites := pendingWrites + 1;
}
action(prepareSend, "ps", desc="Copies received memory data to TBE") {
assert(is_valid(tbe));
peek(memQueue_in, MemoryMsg) {
tbe.dataBlk := in_msg.DataBlk;
}
tbe.rxtxBytes := 0;
tbe.dataBlkValid.setMask(addressOffset(tbe.accAddr, tbe.addr), tbe.accSize);
}
action(copyWriteDataToTBE, "cpWDat", desc="Copies received net data to TBE") {
peek(datInPort, CHIDataMsg) {
assert(is_valid(tbe));
tbe.dataBlk.copyPartial(in_msg.dataBlk, in_msg.bitMask);
tbe.dataBlkValid.orMask(in_msg.bitMask);
tbe.rxtxBytes := tbe.rxtxBytes + in_msg.bitMask.count();
}
}
action(sendDataAndCheck, "sd", desc="Send received data to requestor") {
assert(is_valid(tbe));
assert(tbe.rxtxBytes < blockSize);
enqueue(datOutPort, CHIDataMsg, data_latency) {
out_msg.addr := tbe.addr;
if (tbe.useDataSepResp) {
out_msg.type := CHIDataType:DataSepResp_UC;
} else {
out_msg.type := CHIDataType:CompData_UC;
}
out_msg.dataBlk := tbe.dataBlk;
// Called in order for the whole block so use rxtxBytes as offset
out_msg.bitMask.setMask(tbe.rxtxBytes, data_channel_size);
out_msg.Destination.add(tbe.destination);
}
//DPRINTF(RubySlicc, "rxtxBytes=%d\n", tbe.rxtxBytes);
tbe.rxtxBytes := tbe.rxtxBytes + data_channel_size;
// end or send next chunk next cycle
Event next := Event:Trigger_SendDone;
Cycles delay := intToCycles(0);
if (tbe.rxtxBytes < blockSize) {
next := Event:Trigger_Send;
delay := intToCycles(1);
}
enqueue(triggerOutPort, TriggerMsg, delay) {
out_msg.addr := address;
out_msg.event := next;
}
}
action(checkForReceiveCompletion, "cWc", desc="Check if all data is received") {
assert(is_valid(tbe));
DPRINTF(RubySlicc, "rxtxBytes=%d\n", tbe.rxtxBytes);
assert((tbe.rxtxBytes <= tbe.accSize) && (tbe.rxtxBytes > 0));
if (tbe.rxtxBytes == tbe.accSize) {
enqueue(triggerOutPort, TriggerMsg, 0) {
out_msg.addr := address;
out_msg.event := Event:Trigger_ReceiveDone;
}
tbe.rxtxBytes := 0;
assert(tbe.dataBlkValid.getMask(addressOffset(tbe.accAddr, tbe.addr), tbe.accSize));
}
}
action(popReqInQueue, "preq", desc="Pop request queue.") {
reqRdyInPort.dequeue(clockEdge());
}
action(popDataInQueue, "pdata", desc="Pop data queue.") {
datInPort.dequeue(clockEdge());
}
action(popTriggerQueue, "ptrigger", desc="Pop trigger queue.") {
triggerInPort.dequeue(clockEdge());
}
action(popMemoryQueue, "pmem", desc="Pop memory queue.") {
memQueue_in.dequeue(clockEdge());
}
// Stall/wake-up only used for requests that arrive when we are on the
// WAITING_NET_DATA state. For all other case the line should be either
// ready or we can overlap
action(stallRequestQueue, "str", desc="Stall and wait on the address") {
peek(reqRdyInPort, CHIRequestMsg){
stall_and_wait(reqRdyInPort, address);
}
}
action(wakeUpStalled, "wa", desc="Wake up any requests waiting for this address") {
wakeUpAllBuffers(address);
}
action(sendRetryAck, desc="") {
peek(triggerInPort, TriggerMsg) {
enqueue(rspOutPort, CHIResponseMsg, response_latency) {
out_msg.addr := in_msg.addr;
out_msg.type := CHIResponseType:RetryAck;
out_msg.responder := machineID;
out_msg.Destination.add(in_msg.retryDest);
}
}
}
action(sendPCrdGrant, desc="") {
peek(triggerInPort, TriggerMsg) {
enqueue(rspOutPort, CHIResponseMsg, response_latency) {
out_msg.addr := in_msg.addr;
out_msg.type := CHIResponseType:PCrdGrant;
out_msg.responder := machineID;
out_msg.Destination.add(in_msg.retryDest);
}
}
}
////////////////////////////////////////////////////////////////////////////
// Transitions
////////////////////////////////////////////////////////////////////////////
transition(READY, ReadNoSnp, READING_MEM) {
allocateTBE;
initializeFromReqTBE;
sendMemoryRead;
popReqInQueue;
}
transition(READY, ReadNoSnpSep, READING_MEM) {
allocateTBE;
initializeFromReqTBE;
sendMemoryRead;
sendReadReceipt;
popReqInQueue;
}
transition(READING_MEM, MemoryData, SENDING_NET_DATA) {
prepareSend;
sendDataAndCheck;
popMemoryQueue;
}
transition(SENDING_NET_DATA, Trigger_Send) {
sendDataAndCheck;
popTriggerQueue;
}
transition(READY, WriteNoSnpPtl, WAITING_NET_DATA) {
allocateTBE;
initializeFromReqTBE;
sendCompDBIDResp;
popReqInQueue;
}
transition(READY, WriteNoSnp, WAITING_NET_DATA) {
allocateTBE;
initializeFromReqTBE;
sendCompDBIDResp;
popReqInQueue;
}
transition(WAITING_NET_DATA, WriteData) {
copyWriteDataToTBE;
checkForReceiveCompletion;
popDataInQueue;
}
transition(WAITING_NET_DATA, Trigger_ReceiveDone, READY) {
sendMemoryWrite;
deallocateTBE;
wakeUpStalled;
popTriggerQueue;
}
transition(SENDING_NET_DATA, Trigger_SendDone, READY) {
deallocateTBE;
wakeUpStalled;
popTriggerQueue;
}
// Just sanity check against counter of pending acks
transition({READING_MEM,WAITING_NET_DATA,SENDING_NET_DATA,READY},
MemoryAck) {
decWritePending;
popMemoryQueue;
}
// Notice we only use this here and call wakeUp when leaving this state
transition({READING_MEM,WAITING_NET_DATA,SENDING_NET_DATA},
{ReadNoSnp, ReadNoSnpSep, WriteNoSnpPtl}) {
stallRequestQueue;
}
transition({READING_MEM,WAITING_NET_DATA,SENDING_NET_DATA,READY},
Trigger_SendRetry) {
sendRetryAck;
popTriggerQueue;
}
transition({READING_MEM,WAITING_NET_DATA,SENDING_NET_DATA,READY},
Trigger_SendPCrdGrant) {
sendPCrdGrant;
popTriggerQueue;
}
transition({READING_MEM,WAITING_NET_DATA,SENDING_NET_DATA,READY},
CheckAllocTBE) {
checkAllocateTBE;
}
transition({READING_MEM,WAITING_NET_DATA,SENDING_NET_DATA,READY},
CheckAllocTBE_WithCredit) {
checkAllocateTBE_withCredit;
}
}