| /* | |
| * Copyright (c) 1999-2013 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. | |
| */ | |
| machine(Directory, "Directory protocol") | |
| : DirectoryMemory * directory; | |
| Cycles directory_latency := 6; | |
| Cycles to_memory_controller_latency := 1; | |
| // Message Queues | |
| MessageBuffer * requestToDir, network="From", virtual_network="1", | |
| ordered="false", vnet_type="request"; // a mod-L2 bank -> this Dir | |
| MessageBuffer * responseToDir, network="From", virtual_network="2", | |
| ordered="false", vnet_type="response"; // a mod-L2 bank -> this Dir | |
| MessageBuffer * forwardFromDir, network="To", virtual_network="1", | |
| ordered="false", vnet_type="forward"; | |
| MessageBuffer * responseFromDir, network="To", virtual_network="2", | |
| ordered="false", vnet_type="response"; // Dir -> mod-L2 bank | |
| { | |
| // STATES | |
| state_declaration(State, desc="Directory states", default="Directory_State_I") { | |
| // Base states | |
| I, AccessPermission:Read_Write, desc="Invalid"; | |
| S, AccessPermission:Read_Only, desc="Shared"; | |
| O, AccessPermission:Maybe_Stale, desc="Owner"; | |
| M, AccessPermission:Maybe_Stale, desc="Modified"; | |
| IS, AccessPermission:Busy, desc="Blocked, was in idle"; | |
| SS, AccessPermission:Read_Only, desc="Blocked, was in shared"; | |
| OO, AccessPermission:Busy, desc="Blocked, was in owned"; | |
| MO, AccessPermission:Busy, desc="Blocked, going to owner or maybe modified"; | |
| MM, AccessPermission:Busy, desc="Blocked, going to modified"; | |
| MM_DMA, AccessPermission:Busy, desc="Blocked, going to I"; | |
| MI, AccessPermission:Busy, desc="Blocked on a writeback"; | |
| MIS, AccessPermission:Busy, desc="Blocked on a writeback, but don't remove from sharers when received"; | |
| OS, AccessPermission:Busy, desc="Blocked on a writeback"; | |
| OSS, AccessPermission:Busy, desc="Blocked on a writeback, but don't remove from sharers when received"; | |
| XI_M, AccessPermission:Busy, desc="In a stable state, going to I, waiting for the memory controller"; | |
| XI_U, AccessPermission:Busy, desc="In a stable state, going to I, waiting for an unblock"; | |
| OI_D, AccessPermission:Busy, desc="In O, going to I, waiting for data"; | |
| OD, AccessPermission:Busy, desc="In O, waiting for dma ack from L2"; | |
| MD, AccessPermission:Busy, desc="In M, waiting for dma ack from L2"; | |
| } | |
| // Events | |
| enumeration(Event, desc="Directory events") { | |
| GETX, desc="A GETX arrives"; | |
| GETS, desc="A GETS arrives"; | |
| PUTX, desc="A PUTX arrives"; | |
| PUTO, desc="A PUTO arrives"; | |
| PUTO_SHARERS, desc="A PUTO arrives, but don't remove from sharers list"; | |
| Unblock, desc="An unblock message arrives"; | |
| Last_Unblock, desc="An unblock message arrives, we're not waiting for any additional unblocks"; | |
| Exclusive_Unblock, desc="The processor become the exclusive owner (E or M) of the line"; | |
| Clean_Writeback, desc="The final message as part of a PutX/PutS, no data"; | |
| Dirty_Writeback, desc="The final message as part of a PutX/PutS, contains data"; | |
| Memory_Data, desc="Fetched data from memory arrives"; | |
| Memory_Ack, desc="Writeback Ack from memory arrives"; | |
| DMA_READ, desc="DMA Read"; | |
| DMA_WRITE, desc="DMA Write"; | |
| DMA_ACK, desc="DMA Ack"; | |
| Data, desc="Data to directory"; | |
| } | |
| // TYPES | |
| // DirectoryEntry | |
| structure(Entry, desc="...", interface='AbstractEntry') { | |
| State DirectoryState, desc="Directory state"; | |
| NetDest Sharers, desc="Sharers for this block"; | |
| NetDest Owner, desc="Owner of this block"; | |
| int WaitingUnblocks, desc="Number of acks we're waiting for"; | |
| } | |
| structure(TBE, desc="...") { | |
| Address PhysicalAddress, desc="Physical address for this entry"; | |
| int Len, desc="Length of request"; | |
| DataBlock DataBlk, desc="DataBlk"; | |
| MachineID Requestor, desc="original requestor"; | |
| } | |
| structure(TBETable, external = "yes") { | |
| TBE lookup(Address); | |
| void allocate(Address); | |
| void deallocate(Address); | |
| bool isPresent(Address); | |
| } | |
| // ** OBJECTS ** | |
| TBETable TBEs, template="<Directory_TBE>", constructor="m_number_of_TBEs"; | |
| void set_tbe(TBE b); | |
| void unset_tbe(); | |
| Entry getDirectoryEntry(Address addr), return_by_pointer="yes" { | |
| Entry dir_entry := static_cast(Entry, "pointer", directory[addr]); | |
| if (is_valid(dir_entry)) { | |
| return dir_entry; | |
| } | |
| dir_entry := static_cast(Entry, "pointer", | |
| directory.allocate(addr, new Entry)); | |
| return dir_entry; | |
| } | |
| State getState(TBE tbe, Address addr) { | |
| return getDirectoryEntry(addr).DirectoryState; | |
| } | |
| void setState(TBE tbe, Address addr, State state) { | |
| if (directory.isPresent(addr)) { | |
| if (state == State:I) { | |
| assert(getDirectoryEntry(addr).Owner.count() == 0); | |
| assert(getDirectoryEntry(addr).Sharers.count() == 0); | |
| } | |
| if (state == State:S) { | |
| assert(getDirectoryEntry(addr).Owner.count() == 0); | |
| } | |
| if (state == State:O) { | |
| assert(getDirectoryEntry(addr).Owner.count() == 1); | |
| assert(getDirectoryEntry(addr).Sharers.isSuperset(getDirectoryEntry(addr).Owner) == false); | |
| } | |
| if (state == State:M) { | |
| assert(getDirectoryEntry(addr).Owner.count() == 1); | |
| assert(getDirectoryEntry(addr).Sharers.count() == 0); | |
| } | |
| if ((state != State:SS) && (state != State:OO)) { | |
| assert(getDirectoryEntry(addr).WaitingUnblocks == 0); | |
| } | |
| if ( (getDirectoryEntry(addr).DirectoryState != State:I) && (state == State:I) ) { | |
| getDirectoryEntry(addr).DirectoryState := state; | |
| // disable coherence checker | |
| // sequencer.checkCoherence(addr); | |
| } | |
| else { | |
| getDirectoryEntry(addr).DirectoryState := state; | |
| } | |
| } | |
| } | |
| AccessPermission getAccessPermission(Address addr) { | |
| if (directory.isPresent(addr)) { | |
| DPRINTF(RubySlicc, "%s\n", Directory_State_to_permission(getDirectoryEntry(addr).DirectoryState)); | |
| return Directory_State_to_permission(getDirectoryEntry(addr).DirectoryState); | |
| } | |
| DPRINTF(RubySlicc, "AccessPermission_NotPresent\n"); | |
| return AccessPermission:NotPresent; | |
| } | |
| void setAccessPermission(Address addr, State state) { | |
| if (directory.isPresent(addr)) { | |
| getDirectoryEntry(addr).changePermission(Directory_State_to_permission(state)); | |
| } | |
| } | |
| void functionalRead(Address addr, Packet *pkt) { | |
| functionalMemoryRead(pkt); | |
| } | |
| int functionalWrite(Address addr, Packet *pkt) { | |
| int num_functional_writes := 0; | |
| num_functional_writes := num_functional_writes + functionalMemoryWrite(pkt); | |
| return num_functional_writes; | |
| } | |
| // if no sharers, then directory can be considered | |
| // both a sharer and exclusive w.r.t. coherence checking | |
| bool isBlockShared(Address addr) { | |
| if (directory.isPresent(addr)) { | |
| if (getDirectoryEntry(addr).DirectoryState == State:I) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| bool isBlockExclusive(Address addr) { | |
| if (directory.isPresent(addr)) { | |
| if (getDirectoryEntry(addr).DirectoryState == State:I) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| // ** OUT_PORTS ** | |
| out_port(forwardNetwork_out, RequestMsg, forwardFromDir); | |
| out_port(responseNetwork_out, ResponseMsg, responseFromDir); | |
| // ** IN_PORTS ** | |
| in_port(unblockNetwork_in, ResponseMsg, responseToDir) { | |
| if (unblockNetwork_in.isReady()) { | |
| peek(unblockNetwork_in, ResponseMsg) { | |
| if (in_msg.Type == CoherenceResponseType:UNBLOCK) { | |
| if (getDirectoryEntry(in_msg.Addr).WaitingUnblocks == 1) { | |
| trigger(Event:Last_Unblock, in_msg.Addr, | |
| TBEs[in_msg.Addr]); | |
| } else { | |
| trigger(Event:Unblock, in_msg.Addr, | |
| TBEs[in_msg.Addr]); | |
| } | |
| } else if (in_msg.Type == CoherenceResponseType:UNBLOCK_EXCLUSIVE) { | |
| trigger(Event:Exclusive_Unblock, in_msg.Addr, | |
| TBEs[in_msg.Addr]); | |
| } else if (in_msg.Type == CoherenceResponseType:WRITEBACK_DIRTY_DATA) { | |
| trigger(Event:Dirty_Writeback, in_msg.Addr, | |
| TBEs[in_msg.Addr]); | |
| } else if (in_msg.Type == CoherenceResponseType:WRITEBACK_CLEAN_ACK) { | |
| trigger(Event:Clean_Writeback, in_msg.Addr, | |
| TBEs[in_msg.Addr]); | |
| } else if (in_msg.Type == CoherenceResponseType:DATA_EXCLUSIVE) { | |
| trigger(Event:Data, in_msg.Addr, | |
| TBEs[in_msg.Addr]); | |
| } else if (in_msg.Type == CoherenceResponseType:DMA_ACK) { | |
| trigger(Event:DMA_ACK, in_msg.Addr, | |
| TBEs[in_msg.Addr]); | |
| } else { | |
| error("Invalid message"); | |
| } | |
| } | |
| } | |
| } | |
| in_port(requestQueue_in, RequestMsg, requestToDir) { | |
| if (requestQueue_in.isReady()) { | |
| peek(requestQueue_in, RequestMsg) { | |
| if (in_msg.Type == CoherenceRequestType:GETS) { | |
| trigger(Event:GETS, in_msg.Addr, TBEs[in_msg.Addr]); | |
| } else if (in_msg.Type == CoherenceRequestType:GETX) { | |
| trigger(Event:GETX, in_msg.Addr, TBEs[in_msg.Addr]); | |
| } else if (in_msg.Type == CoherenceRequestType:PUTX) { | |
| trigger(Event:PUTX, in_msg.Addr, TBEs[in_msg.Addr]); | |
| } else if (in_msg.Type == CoherenceRequestType:PUTO) { | |
| trigger(Event:PUTO, in_msg.Addr, TBEs[in_msg.Addr]); | |
| } else if (in_msg.Type == CoherenceRequestType:PUTO_SHARERS) { | |
| trigger(Event:PUTO_SHARERS, in_msg.Addr, TBEs[in_msg.Addr]); | |
| } else if (in_msg.Type == CoherenceRequestType:DMA_READ) { | |
| trigger(Event:DMA_READ, makeLineAddress(in_msg.Addr), | |
| TBEs[makeLineAddress(in_msg.Addr)]); | |
| } else if (in_msg.Type == CoherenceRequestType:DMA_WRITE) { | |
| trigger(Event:DMA_WRITE, makeLineAddress(in_msg.Addr), | |
| TBEs[makeLineAddress(in_msg.Addr)]); | |
| } else { | |
| error("Invalid message"); | |
| } | |
| } | |
| } | |
| } | |
| // off-chip memory request/response is done | |
| in_port(memQueue_in, MemoryMsg, responseFromMemory) { | |
| if (memQueue_in.isReady()) { | |
| peek(memQueue_in, MemoryMsg) { | |
| if (in_msg.Type == MemoryRequestType:MEMORY_READ) { | |
| trigger(Event:Memory_Data, in_msg.Addr, TBEs[in_msg.Addr]); | |
| } else if (in_msg.Type == MemoryRequestType:MEMORY_WB) { | |
| trigger(Event:Memory_Ack, in_msg.Addr, TBEs[in_msg.Addr]); | |
| } else { | |
| DPRINTF(RubySlicc, "%s\n", in_msg.Type); | |
| error("Invalid message"); | |
| } | |
| } | |
| } | |
| } | |
| // Actions | |
| action(a_sendWriteBackAck, "a", desc="Send writeback ack to requestor") { | |
| peek(requestQueue_in, RequestMsg) { | |
| enqueue(forwardNetwork_out, RequestMsg, directory_latency) { | |
| out_msg.Addr := address; | |
| out_msg.Type := CoherenceRequestType:WB_ACK; | |
| out_msg.Requestor := in_msg.Requestor; | |
| out_msg.RequestorMachine := MachineType:Directory; | |
| out_msg.Destination.add(in_msg.Requestor); | |
| out_msg.MessageSize := MessageSizeType:Writeback_Control; | |
| } | |
| } | |
| } | |
| action(b_sendWriteBackNack, "b", desc="Send writeback nack to requestor") { | |
| peek(requestQueue_in, RequestMsg) { | |
| enqueue(forwardNetwork_out, RequestMsg, directory_latency) { | |
| out_msg.Addr := address; | |
| out_msg.Type := CoherenceRequestType:WB_NACK; | |
| out_msg.Requestor := in_msg.Requestor; | |
| out_msg.RequestorMachine := MachineType:Directory; | |
| out_msg.Destination.add(in_msg.Requestor); | |
| out_msg.MessageSize := MessageSizeType:Writeback_Control; | |
| } | |
| } | |
| } | |
| action(c_clearOwner, "c", desc="Clear the owner field") { | |
| getDirectoryEntry(address).Owner.clear(); | |
| } | |
| action(c_moveOwnerToSharer, "cc", desc="Move owner to sharers") { | |
| getDirectoryEntry(address).Sharers.addNetDest(getDirectoryEntry(address).Owner); | |
| getDirectoryEntry(address).Owner.clear(); | |
| } | |
| action(cc_clearSharers, "\c", desc="Clear the sharers field") { | |
| getDirectoryEntry(address).Sharers.clear(); | |
| } | |
| action(d_sendDataMsg, "d", desc="Send data to requestor") { | |
| peek(memQueue_in, MemoryMsg) { | |
| enqueue(responseNetwork_out, ResponseMsg, 1) { | |
| out_msg.Addr := address; | |
| out_msg.Sender := machineID; | |
| out_msg.SenderMachine := MachineType:Directory; | |
| out_msg.Destination.add(in_msg.OriginalRequestorMachId); | |
| out_msg.DataBlk := in_msg.DataBlk; | |
| out_msg.Dirty := false; // By definition, the block is now clean | |
| out_msg.Acks := in_msg.Acks; | |
| if (in_msg.ReadX) { | |
| out_msg.Type := CoherenceResponseType:DATA_EXCLUSIVE; | |
| } else { | |
| out_msg.Type := CoherenceResponseType:DATA; | |
| } | |
| out_msg.MessageSize := MessageSizeType:Response_Data; | |
| } | |
| } | |
| } | |
| action(p_fwdDataToDMA, "\d", desc="Send data to requestor") { | |
| peek(requestQueue_in, RequestMsg) { | |
| enqueue(responseNetwork_out, ResponseMsg, 1) { | |
| out_msg.Addr := address; | |
| out_msg.Sender := machineID; | |
| out_msg.SenderMachine := MachineType:Directory; | |
| out_msg.Destination.add(in_msg.Requestor); | |
| out_msg.Dirty := false; // By definition, the block is now clean | |
| out_msg.Type := CoherenceResponseType:DATA_EXCLUSIVE; | |
| out_msg.MessageSize := MessageSizeType:Response_Data; | |
| } | |
| } | |
| } | |
| action(e_ownerIsUnblocker, "e", desc="The owner is now the unblocker") { | |
| peek(unblockNetwork_in, ResponseMsg) { | |
| getDirectoryEntry(address).Owner.clear(); | |
| getDirectoryEntry(address).Owner.add(in_msg.Sender); | |
| } | |
| } | |
| action(f_forwardRequest, "f", desc="Forward request to owner") { | |
| peek(requestQueue_in, RequestMsg) { | |
| enqueue(forwardNetwork_out, RequestMsg, directory_latency) { | |
| out_msg.Addr := address; | |
| out_msg.Type := in_msg.Type; | |
| out_msg.Requestor := in_msg.Requestor; | |
| out_msg.RequestorMachine := machineIDToMachineType(in_msg.Requestor); | |
| out_msg.Destination.addNetDest(getDirectoryEntry(in_msg.Addr).Owner); | |
| out_msg.Acks := getDirectoryEntry(address).Sharers.count(); | |
| if (getDirectoryEntry(address).Sharers.isElement(in_msg.Requestor)) { | |
| out_msg.Acks := out_msg.Acks - 1; | |
| } | |
| out_msg.MessageSize := MessageSizeType:Forwarded_Control; | |
| } | |
| } | |
| } | |
| action(f_forwardRequestDirIsRequestor, "\f", desc="Forward request to owner") { | |
| peek(requestQueue_in, RequestMsg) { | |
| enqueue(forwardNetwork_out, RequestMsg, directory_latency) { | |
| out_msg.Addr := address; | |
| out_msg.Type := in_msg.Type; | |
| out_msg.Requestor := machineID; | |
| out_msg.RequestorMachine := machineIDToMachineType(in_msg.Requestor); | |
| out_msg.Destination.addNetDest(getDirectoryEntry(in_msg.Addr).Owner); | |
| out_msg.Acks := getDirectoryEntry(address).Sharers.count(); | |
| if (getDirectoryEntry(address).Sharers.isElement(in_msg.Requestor)) { | |
| out_msg.Acks := out_msg.Acks - 1; | |
| } | |
| out_msg.MessageSize := MessageSizeType:Forwarded_Control; | |
| } | |
| } | |
| } | |
| action(g_sendInvalidations, "g", desc="Send invalidations to sharers, not including the requester") { | |
| peek(requestQueue_in, RequestMsg) { | |
| if ((getDirectoryEntry(in_msg.Addr).Sharers.count() > 1) || | |
| ((getDirectoryEntry(in_msg.Addr).Sharers.count() > 0) && | |
| (getDirectoryEntry(in_msg.Addr).Sharers.isElement(in_msg.Requestor) == false))) { | |
| enqueue(forwardNetwork_out, RequestMsg, directory_latency) { | |
| out_msg.Addr := address; | |
| out_msg.Type := CoherenceRequestType:INV; | |
| out_msg.Requestor := in_msg.Requestor; | |
| out_msg.RequestorMachine := machineIDToMachineType(in_msg.Requestor); | |
| // out_msg.Destination := getDirectoryEntry(in_msg.Addr).Sharers; | |
| out_msg.Destination.addNetDest(getDirectoryEntry(in_msg.Addr).Sharers); | |
| out_msg.Destination.remove(in_msg.Requestor); | |
| out_msg.MessageSize := MessageSizeType:Invalidate_Control; | |
| } | |
| } | |
| } | |
| } | |
| action(i_popIncomingRequestQueue, "i", desc="Pop incoming request queue") { | |
| requestQueue_in.dequeue(); | |
| } | |
| action(j_popIncomingUnblockQueue, "j", desc="Pop incoming unblock queue") { | |
| unblockNetwork_in.dequeue(); | |
| } | |
| action(m_addUnlockerToSharers, "m", desc="Add the unlocker to the sharer list") { | |
| peek(unblockNetwork_in, ResponseMsg) { | |
| getDirectoryEntry(address).Sharers.add(in_msg.Sender); | |
| } | |
| } | |
| action(n_incrementOutstanding, "n", desc="Increment outstanding requests") { | |
| getDirectoryEntry(address).WaitingUnblocks := getDirectoryEntry(address).WaitingUnblocks + 1; | |
| } | |
| action(o_decrementOutstanding, "o", desc="Decrement outstanding requests") { | |
| getDirectoryEntry(address).WaitingUnblocks := getDirectoryEntry(address).WaitingUnblocks - 1; | |
| assert(getDirectoryEntry(address).WaitingUnblocks >= 0); | |
| } | |
| action(q_popMemQueue, "q", desc="Pop off-chip request queue") { | |
| memQueue_in.dequeue(); | |
| } | |
| action(qf_queueMemoryFetchRequest, "qf", desc="Queue off-chip fetch request") { | |
| peek(requestQueue_in, RequestMsg) { | |
| queueMemoryRead(in_msg.Requestor, address, to_memory_controller_latency); | |
| } | |
| } | |
| action(qw_queueMemoryWBRequest, "qw", desc="Queue off-chip writeback request") { | |
| peek(unblockNetwork_in, ResponseMsg) { | |
| if (is_valid(tbe)) { | |
| queueMemoryWrite(tbe.Requestor, address, to_memory_controller_latency, | |
| in_msg.DataBlk); | |
| } else { | |
| queueMemoryWrite(in_msg.Sender, address, to_memory_controller_latency, | |
| in_msg.DataBlk); | |
| } | |
| } | |
| } | |
| action(qw_queueMemoryWBRequestFromMessageAndTBE, "qwmt", | |
| desc="Queue off-chip writeback request") { | |
| peek(unblockNetwork_in, ResponseMsg) { | |
| DataBlock DataBlk := in_msg.DataBlk; | |
| DataBlk.copyPartial(tbe.DataBlk, addressOffset(tbe.PhysicalAddress), | |
| tbe.Len); | |
| queueMemoryWrite(tbe.Requestor, address, to_memory_controller_latency, | |
| DataBlk); | |
| } | |
| } | |
| action(qw_queueMemoryWBRequest2, "/qw", desc="Queue off-chip writeback request") { | |
| peek(requestQueue_in, RequestMsg) { | |
| queueMemoryWrite(in_msg.Requestor, address, to_memory_controller_latency, | |
| in_msg.DataBlk); | |
| } | |
| } | |
| action(zz_recycleRequest, "\z", desc="Recycle the request queue") { | |
| requestQueue_in.recycle(); | |
| } | |
| action(a_sendDMAAck, "\a", desc="Send DMA Ack that write completed, along with Inv Ack count") { | |
| peek(requestQueue_in, RequestMsg) { | |
| enqueue(responseNetwork_out, ResponseMsg, 1) { | |
| out_msg.Addr := address; | |
| out_msg.Sender := machineID; | |
| out_msg.SenderMachine := MachineType:Directory; | |
| out_msg.Destination.add(in_msg.Requestor); | |
| out_msg.DataBlk := in_msg.DataBlk; | |
| out_msg.Acks := getDirectoryEntry(address).Sharers.count(); // for dma requests | |
| out_msg.Type := CoherenceResponseType:DMA_ACK; | |
| out_msg.MessageSize := MessageSizeType:Writeback_Control; | |
| } | |
| } | |
| } | |
| action(a_sendDMAAck2, "\aa", desc="Send DMA Ack that write completed, along with Inv Ack count") { | |
| peek(unblockNetwork_in, ResponseMsg) { | |
| enqueue(responseNetwork_out, ResponseMsg, 1) { | |
| out_msg.Addr := address; | |
| out_msg.Sender := machineID; | |
| out_msg.SenderMachine := MachineType:Directory; | |
| if (is_valid(tbe)) { | |
| out_msg.Destination.add(tbe.Requestor); | |
| } | |
| out_msg.DataBlk := in_msg.DataBlk; | |
| out_msg.Acks := getDirectoryEntry(address).Sharers.count(); // for dma requests | |
| out_msg.Type := CoherenceResponseType:DMA_ACK; | |
| out_msg.MessageSize := MessageSizeType:Writeback_Control; | |
| } | |
| } | |
| } | |
| action(v_allocateTBE, "v", desc="Allocate TBE entry") { | |
| peek (requestQueue_in, RequestMsg) { | |
| TBEs.allocate(address); | |
| set_tbe(TBEs[address]); | |
| tbe.PhysicalAddress := in_msg.Addr; | |
| tbe.Len := in_msg.Len; | |
| tbe.DataBlk := in_msg.DataBlk; | |
| tbe.Requestor := in_msg.Requestor; | |
| } | |
| } | |
| action(w_deallocateTBE, "w", desc="Deallocate TBE entry") { | |
| TBEs.deallocate(address); | |
| unset_tbe(); | |
| } | |
| // TRANSITIONS | |
| transition(I, GETX, MM) { | |
| qf_queueMemoryFetchRequest; | |
| i_popIncomingRequestQueue; | |
| } | |
| transition(I, DMA_READ, XI_M) { | |
| qf_queueMemoryFetchRequest; | |
| i_popIncomingRequestQueue; | |
| } | |
| transition(I, DMA_WRITE, XI_U) { | |
| qw_queueMemoryWBRequest2; | |
| a_sendDMAAck; // ack count may be zero | |
| i_popIncomingRequestQueue; | |
| } | |
| transition(XI_M, Memory_Data, I) { | |
| d_sendDataMsg; // ack count may be zero | |
| q_popMemQueue; | |
| } | |
| transition(XI_U, Exclusive_Unblock, I) { | |
| cc_clearSharers; | |
| c_clearOwner; | |
| j_popIncomingUnblockQueue; | |
| } | |
| transition(S, GETX, MM) { | |
| qf_queueMemoryFetchRequest; | |
| g_sendInvalidations; | |
| i_popIncomingRequestQueue; | |
| } | |
| transition(S, DMA_READ) { | |
| //qf_queueMemoryFetchRequest; | |
| p_fwdDataToDMA; | |
| //g_sendInvalidations; // the DMA will collect the invalidations then send an Unblock Exclusive | |
| i_popIncomingRequestQueue; | |
| } | |
| transition(S, DMA_WRITE, XI_U) { | |
| qw_queueMemoryWBRequest2; | |
| a_sendDMAAck; // ack count may be zero | |
| g_sendInvalidations; // the DMA will collect invalidations | |
| i_popIncomingRequestQueue; | |
| } | |
| transition(I, GETS, IS) { | |
| qf_queueMemoryFetchRequest; | |
| i_popIncomingRequestQueue; | |
| } | |
| transition({S, SS}, GETS, SS) { | |
| qf_queueMemoryFetchRequest; | |
| n_incrementOutstanding; | |
| i_popIncomingRequestQueue; | |
| } | |
| transition({I, S}, PUTO) { | |
| b_sendWriteBackNack; | |
| i_popIncomingRequestQueue; | |
| } | |
| transition({I, S, O}, PUTX) { | |
| b_sendWriteBackNack; | |
| i_popIncomingRequestQueue; | |
| } | |
| transition(O, GETX, MM) { | |
| f_forwardRequest; | |
| g_sendInvalidations; | |
| i_popIncomingRequestQueue; | |
| } | |
| transition(O, DMA_READ, OD) { | |
| f_forwardRequest; // this will cause the data to go to DMA directly | |
| //g_sendInvalidations; // this will cause acks to be sent to the DMA | |
| i_popIncomingRequestQueue; | |
| } | |
| transition(OD, DMA_ACK, O) { | |
| j_popIncomingUnblockQueue; | |
| } | |
| transition({O,M}, DMA_WRITE, OI_D) { | |
| f_forwardRequestDirIsRequestor; // need the modified data before we can proceed | |
| g_sendInvalidations; // these go to the DMA Controller | |
| v_allocateTBE; | |
| i_popIncomingRequestQueue; | |
| } | |
| transition(OI_D, Data, XI_U) { | |
| qw_queueMemoryWBRequestFromMessageAndTBE; | |
| a_sendDMAAck2; // ack count may be zero | |
| w_deallocateTBE; | |
| j_popIncomingUnblockQueue; | |
| } | |
| transition({O, OO}, GETS, OO) { | |
| f_forwardRequest; | |
| n_incrementOutstanding; | |
| i_popIncomingRequestQueue; | |
| } | |
| transition(M, GETX, MM) { | |
| f_forwardRequest; | |
| i_popIncomingRequestQueue; | |
| } | |
| // no exclusive unblock will show up to the directory | |
| transition(M, DMA_READ, MD) { | |
| f_forwardRequest; // this will cause the data to go to DMA directly | |
| i_popIncomingRequestQueue; | |
| } | |
| transition(MD, DMA_ACK, M) { | |
| j_popIncomingUnblockQueue; | |
| } | |
| transition(M, GETS, MO) { | |
| f_forwardRequest; | |
| i_popIncomingRequestQueue; | |
| } | |
| transition(M, PUTX, MI) { | |
| a_sendWriteBackAck; | |
| i_popIncomingRequestQueue; | |
| } | |
| // happens if M->O transition happens on-chip | |
| transition(M, PUTO, MI) { | |
| a_sendWriteBackAck; | |
| i_popIncomingRequestQueue; | |
| } | |
| transition(M, PUTO_SHARERS, MIS) { | |
| a_sendWriteBackAck; | |
| i_popIncomingRequestQueue; | |
| } | |
| transition(O, PUTO, OS) { | |
| a_sendWriteBackAck; | |
| i_popIncomingRequestQueue; | |
| } | |
| transition(O, PUTO_SHARERS, OSS) { | |
| a_sendWriteBackAck; | |
| i_popIncomingRequestQueue; | |
| } | |
| transition({MM, MO, MI, MIS, OS, OSS, XI_M, XI_U, OI_D, OD, MD}, {GETS, GETX, PUTO, PUTO_SHARERS, PUTX, DMA_READ, DMA_WRITE}) { | |
| zz_recycleRequest; | |
| } | |
| transition({MM, MO}, Exclusive_Unblock, M) { | |
| cc_clearSharers; | |
| e_ownerIsUnblocker; | |
| j_popIncomingUnblockQueue; | |
| } | |
| transition(MO, Unblock, O) { | |
| m_addUnlockerToSharers; | |
| j_popIncomingUnblockQueue; | |
| } | |
| transition({IS, SS, OO}, {GETX, PUTO, PUTO_SHARERS, PUTX, DMA_READ, DMA_WRITE}) { | |
| zz_recycleRequest; | |
| } | |
| transition(IS, GETS) { | |
| zz_recycleRequest; | |
| } | |
| transition(IS, Unblock, S) { | |
| m_addUnlockerToSharers; | |
| j_popIncomingUnblockQueue; | |
| } | |
| transition(IS, Exclusive_Unblock, M) { | |
| cc_clearSharers; | |
| e_ownerIsUnblocker; | |
| j_popIncomingUnblockQueue; | |
| } | |
| transition(SS, Unblock) { | |
| m_addUnlockerToSharers; | |
| o_decrementOutstanding; | |
| j_popIncomingUnblockQueue; | |
| } | |
| transition(SS, Last_Unblock, S) { | |
| m_addUnlockerToSharers; | |
| o_decrementOutstanding; | |
| j_popIncomingUnblockQueue; | |
| } | |
| transition(OO, Unblock) { | |
| m_addUnlockerToSharers; | |
| o_decrementOutstanding; | |
| j_popIncomingUnblockQueue; | |
| } | |
| transition(OO, Last_Unblock, O) { | |
| m_addUnlockerToSharers; | |
| o_decrementOutstanding; | |
| j_popIncomingUnblockQueue; | |
| } | |
| transition(MI, Dirty_Writeback, I) { | |
| c_clearOwner; | |
| cc_clearSharers; | |
| qw_queueMemoryWBRequest; | |
| j_popIncomingUnblockQueue; | |
| } | |
| transition(MIS, Dirty_Writeback, S) { | |
| c_moveOwnerToSharer; | |
| qw_queueMemoryWBRequest; | |
| j_popIncomingUnblockQueue; | |
| } | |
| transition(MIS, Clean_Writeback, S) { | |
| c_moveOwnerToSharer; | |
| j_popIncomingUnblockQueue; | |
| } | |
| transition(OS, Dirty_Writeback, S) { | |
| c_clearOwner; | |
| qw_queueMemoryWBRequest; | |
| j_popIncomingUnblockQueue; | |
| } | |
| transition(OSS, Dirty_Writeback, S) { | |
| c_moveOwnerToSharer; | |
| qw_queueMemoryWBRequest; | |
| j_popIncomingUnblockQueue; | |
| } | |
| transition(OSS, Clean_Writeback, S) { | |
| c_moveOwnerToSharer; | |
| j_popIncomingUnblockQueue; | |
| } | |
| transition(MI, Clean_Writeback, I) { | |
| c_clearOwner; | |
| cc_clearSharers; | |
| j_popIncomingUnblockQueue; | |
| } | |
| transition(OS, Clean_Writeback, S) { | |
| c_clearOwner; | |
| j_popIncomingUnblockQueue; | |
| } | |
| transition({MI, MIS}, Unblock, M) { | |
| j_popIncomingUnblockQueue; | |
| } | |
| transition({OS, OSS}, Unblock, O) { | |
| j_popIncomingUnblockQueue; | |
| } | |
| transition({I, S, O, M, IS, SS, OO, MO, MM, MI, MIS, OS, OSS}, Memory_Data) { | |
| d_sendDataMsg; | |
| q_popMemQueue; | |
| } | |
| transition({I, S, O, M, IS, SS, OO, MO, MM, MI, MIS, OS, OSS, XI_U, XI_M}, Memory_Ack) { | |
| //a_sendAck; | |
| q_popMemQueue; | |
| } | |
| } |