blob: a1043220cae4a39f68bd1ef0411d25cd2b19ff88 [file] [log] [blame]
/*
* Copyright (c) 2021-2022 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.
*/
////////////////////////////////////////////////////////////////////////////
// CHI-dvm-misc-node actions definitions
////////////////////////////////////////////////////////////////////////////
action(AllocateTBE_Request, desc="") {
peek(reqInPort, CHIRequestMsg) {
assert(in_msg.usesTxnId);
assert(in_msg.txnId == address);
assert(in_msg.is_local_pf == false);
assert(in_msg.allowRetry);
bool isNonSync := (in_msg.type == CHIRequestType:DvmOpNonSync);
if (storDvmTBEs.areNSlotsAvailable(1, tbePartition(isNonSync))) {
// reserve a slot for this request
storDvmTBEs.incrementReserved(tbePartition(isNonSync));
// Move request to rdy queue
peek(reqInPort, CHIRequestMsg) {
enqueue(reqRdyOutPort, CHIRequestMsg, allocation_latency) {
out_msg := in_msg;
}
}
} else {
// we don't have resources to track this request; enqueue a retry
enqueue(retryTriggerOutPort, RetryTriggerMsg, retry_ack_latency) {
out_msg.txnId := in_msg.txnId;
out_msg.event := Event:SendRetryAck;
out_msg.retryDest := in_msg.requestor;
RetryQueueEntry en;
en.txnId := in_msg.txnId;
en.retryDest := in_msg.requestor;
en.isNonSync := isNonSync;
storDvmTBEs.emplaceRetryEntry(en);
}
}
}
reqInPort.dequeue(clockEdge());
}
action(AllocateTBE_Request_WithCredit, desc="") {
// TBE slot already reserved
// Move request to rdy queue
peek(reqInPort, CHIRequestMsg) {
assert(in_msg.allowRetry == false);
assert(in_msg.usesTxnId);
enqueue(reqRdyOutPort, CHIRequestMsg, allocation_latency) {
assert(in_msg.txnId == address);
out_msg := in_msg;
}
}
reqInPort.dequeue(clockEdge());
}
action(Initiate_Request_DVM, desc="") {
bool was_retried := false;
peek(reqRdyPort, CHIRequestMsg) {
// "address" for DVM = transaction ID
TBE tbe := allocateDvmRequestTBE(address, in_msg);
set_tbe(tbe);
// only a msg that was already retried doesn't allow a retry
was_retried := in_msg.allowRetry == false;
}
// Last argument = false, so it isn't treated as a memory request
incomingTransactionStart(address, curTransitionEvent(), State:Unallocated, was_retried, false);
}
action(Pop_ReqRdyQueue, desc="") {
reqRdyPort.dequeue(clockEdge());
}
action(Receive_ReqDataResp, desc="") {
assert(is_valid(tbe));
assert(tbe.expected_req_resp.hasExpected());
peek(datInPort, CHIDataMsg) {
// Decrement pending
if (tbe.expected_req_resp.receiveData(in_msg.type) == false) {
error("Received unexpected message");
}
assert(!tbe.expected_req_resp.hasExpected());
DPRINTF(RubyProtocol, "Misc Node has receieved NCBWrData\n");
// Clear the "expected types" list to prepare for the snooping
tbe.expected_snp_resp.clear(1);
assert(!tbe.expected_snp_resp.hasExpected());
// We don't actually use the data contents
}
}
action(Pop_DataInQueue, desc="") {
datInPort.dequeue(clockEdge());
}
action(Receive_SnpResp, desc="") {
assert(tbe.expected_snp_resp.hasExpected());
peek(rspInPort, CHIResponseMsg) {
// Decrement pending
if (tbe.expected_snp_resp.receiveResp(in_msg.type) == false) {
error("Received unexpected message");
}
assert(in_msg.stale == false);
// assert(in_msg.stale == tbe.is_stale);
DPRINTF(RubyProtocol, "Misc Node has receieved SnpResp_I\n");
assert(tbe.pendingTargets.isElement(in_msg.responder));
tbe.pendingTargets.remove(in_msg.responder);
tbe.receivedTargets.add(in_msg.responder);
}
}
action(Pop_RespInQueue, desc="") {
rspInPort.dequeue(clockEdge());
}
action(ProcessNextState, desc="") {
assert(is_valid(tbe));
processNextState(tbe);
}
// If ProcessNextState invokes a new action, it sets tbe.pendingAction.
// If you call ProcessNextState again without clearing this variable,
// nothing will happen. This Action clears the pending state, ensuring
// a new state is processed.
action(ProcessNextState_ClearPending, desc="") {
assert(is_valid(tbe));
clearPendingAction(tbe);
processNextState(tbe);
}
action(Send_Comp, desc="") {
assert(is_valid(tbe));
Cycles latency := response_latency;
CHIResponseType type := CHIResponseType:Comp;
enqueue(rspOutPort, CHIResponseMsg, latency) {
out_msg.addr := address;
out_msg.type := type;
out_msg.responder := machineID;
out_msg.txnId := address;
out_msg.usesTxnId := true;
out_msg.Destination.add(tbe.requestor);
}
DPRINTF(RubyProtocol, "Misc Node Sending Comp (for either)\n");
}
action(Send_Comp_NonSync, desc="") {
assert(is_valid(tbe));
// In the NonSync case, if early_nonsync_comp is set then
// we will have already sent a CompDBIDResp.
// Thus, only send Comp if !early_nonsync_comp
if (!early_nonsync_comp) {
Cycles latency := response_latency;
CHIResponseType type := CHIResponseType:Comp;
enqueue(rspOutPort, CHIResponseMsg, latency) {
out_msg.addr := address;
out_msg.type := type;
out_msg.responder := machineID;
out_msg.txnId := address;
out_msg.usesTxnId := true;
out_msg.Destination.add(tbe.requestor);
}
DPRINTF(RubyProtocol, "Misc Node Sending TLBI Comp\n");
}
}
// NOTICE a trigger event may wakeup another stalled trigger event so
// this is always called first in the transitions so we don't pop the
// wrong message
action(Pop_TriggerQueue, desc="") {
triggerInPort.dequeue(clockEdge());
}
action(Pop_RetryTriggerQueue, desc="") {
retryTriggerInPort.dequeue(clockEdge());
}
action(Finalize_DeallocateRequest, desc="") {
assert(is_valid(tbe));
assert(tbe.actions.empty());
wakeupPendingReqs(tbe);
wakeupPendingTgrs(tbe);
DPRINTF(RubyProtocol, "Deallocating DVM request\n");
deallocateDvmTBE(tbe);
processRetryQueue();
unset_tbe();
// Last argument = false, so this isn't treated as a memory transaction
incomingTransactionEnd(address, curTransitionNextState(), false);
}
action(Send_DvmNonSyncDBIDResp, desc="") {
Cycles latency := response_latency;
CHIResponseType type := CHIResponseType:DBIDResp;
if (early_nonsync_comp) {
type := CHIResponseType:CompDBIDResp;
}
enqueue(rspOutPort, CHIResponseMsg, latency) {
out_msg.addr := address;
out_msg.type := type;
out_msg.responder := machineID;
out_msg.txnId := address;
out_msg.usesTxnId := true;
out_msg.Destination.add(tbe.requestor);
}
tbe.expected_req_resp.clear(1);
tbe.expected_req_resp.addExpectedDataType(CHIDataType:NCBWrData);
tbe.expected_req_resp.setExpectedCount(1);
DPRINTF(RubyProtocol, "Misc Node Sending TLBI DBIDResp\n");
}
action(Send_DvmSyncDBIDResp, desc="") {
Cycles latency := response_latency;
CHIResponseType type := CHIResponseType:DBIDResp;
enqueue(rspOutPort, CHIResponseMsg, latency) {
out_msg.addr := address;
out_msg.type := type;
out_msg.responder := machineID;
out_msg.txnId := address;
out_msg.usesTxnId := true;
out_msg.Destination.add(tbe.requestor);
}
tbe.expected_req_resp.clear(1);
tbe.expected_req_resp.addExpectedDataType(CHIDataType:NCBWrData);
tbe.expected_req_resp.setExpectedCount(1);
DPRINTF(RubyProtocol, "Misc Node Sending Sync DBIDResp\n");
}
action(Send_RetryAck, desc="") {
peek(retryTriggerInPort, RetryTriggerMsg) {
enqueue(rspOutPort, CHIResponseMsg, response_latency) {
out_msg.txnId := in_msg.txnId;
out_msg.usesTxnId := true;
out_msg.type := CHIResponseType:RetryAck;
out_msg.responder := machineID;
out_msg.Destination.add(in_msg.retryDest);
}
DPRINTF(RubyProtocol, "Misc Node Sending RetryAck (for either)\n");
}
}
action(Send_PCrdGrant, desc="") {
peek(retryTriggerInPort, RetryTriggerMsg) {
enqueue(rspOutPort, CHIResponseMsg, response_latency) {
out_msg.txnId := in_msg.txnId;
out_msg.usesTxnId := true;
out_msg.type := CHIResponseType:PCrdGrant;
out_msg.responder := machineID;
out_msg.Destination.add(in_msg.retryDest);
}
DPRINTF(RubyProtocol, "Misc Node Sending PCrdGrant (for either)\n");
}
}
action(Send_DvmSnoop_P1, desc="") {
Cycles latency := response_latency;
CHIRequestType type := CHIRequestType:SnpDvmOpSync_P1;
if (tbe.isNonSync) {
type := CHIRequestType:SnpDvmOpNonSync_P1;
}
assert(tbe.notSentTargets.count() > 0);
MachineID target := tbe.notSentTargets.smallestElement();
enqueue(snpOutPort, CHIRequestMsg, latency) {
prepareRequest(tbe, type, out_msg);
DPRINTF(RubyProtocol, "Misc Node Sending %d to %d\n", type, target);
out_msg.usesTxnId := true;
out_msg.txnId := tbe.txnId; // for DVM TBEs addr = txnId
out_msg.allowRetry := false;
out_msg.Destination.clear();
out_msg.Destination.add(target);
out_msg.dataToFwdRequestor := false;
}
tbe.actions.pushNB(Event:DvmSendNextMessage_P2);
tbe.delayNextAction := curTick() + cyclesToTicks(intToCycles(1));
// We are no longer waiting on other transaction activity
tbe.waiting_on_other_txns := false;
}
action(Send_DvmSnoop_P2, desc="") {
Cycles latency := response_latency;
CHIRequestType type := CHIRequestType:SnpDvmOpSync_P2;
if (tbe.isNonSync) {
type := CHIRequestType:SnpDvmOpNonSync_P2;
}
assert(tbe.notSentTargets.count() > 0);
MachineID target := tbe.notSentTargets.smallestElement();
enqueue(snpOutPort, CHIRequestMsg, latency) {
prepareRequest(tbe, type, out_msg);
DPRINTF(RubyProtocol, "Misc Node Sending %d to %d\n", type, target);
out_msg.usesTxnId := true;
out_msg.txnId := tbe.txnId; // for DVM TBEs addr = txnId
out_msg.allowRetry := false;
out_msg.Destination.clear();
out_msg.Destination.add(target);
out_msg.dataToFwdRequestor := false;
}
// Expect a SnpResp_I now we have sent both
tbe.expected_snp_resp.addExpectedRespType(CHIResponseType:SnpResp_I);
tbe.expected_snp_resp.addExpectedCount(1);
// Pop the target we just completed off the list
tbe.notSentTargets.remove(target);
tbe.pendingTargets.add(target);
// If we have more targets, enqueue another send
if (tbe.notSentTargets.count() > 0) {
tbe.actions.pushNB(Event:DvmSendNextMessage_P1);
} else {
// otherwise enqueue a DvmFinishDistributing, then a blocking DvmFinishWaiting
// DvmFinishDistributing will be called immediately,
// DvmFinishWaiting will be called once all responses are received
tbe.actions.pushNB(Event:DvmFinishDistributing);
tbe.actions.push(Event:DvmFinishWaiting);
}
tbe.delayNextAction := curTick() + cyclesToTicks(intToCycles(1));
}
action(Enqueue_UpdatePendingOps, desc="") {
// The next time updatePendingOps runs
// it will check this variable and decide
// to actually check for a new sender
DPRINTF(RubyProtocol, "Enqueue_UpdatePendingOps from %016x\n", address);
needsToCheckPendingOps := true;
// Schedule a generic event to make sure we wake up
// on the next tick
scheduleEvent(intToCycles(1));
}
action(Profile_OutgoingEnd_DVM, desc="") {
assert(is_valid(tbe));
// Outgoing transactions = time to send all snoops
// Is never rejected by recipient => never received retry ack
bool rcvdRetryAck := false;
outgoingTransactionEnd(address, rcvdRetryAck, false);
}