| |
| machine(L1Cache, "MI Example L1 Cache") |
| : Sequencer * sequencer, |
| CacheMemory * cacheMemory, |
| int cache_response_latency = 12, |
| int issue_latency = 2 |
| { |
| |
| // NETWORK BUFFERS |
| MessageBuffer requestFromCache, network="To", virtual_network="2", ordered="true", vnet_type="request"; |
| MessageBuffer responseFromCache, network="To", virtual_network="4", ordered="true", vnet_type="response"; |
| |
| MessageBuffer forwardToCache, network="From", virtual_network="3", ordered="true", vnet_type="forward"; |
| MessageBuffer responseToCache, network="From", virtual_network="4", ordered="true", vnet_type="response"; |
| |
| // 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 |
| |
| MessageBuffer mandatoryQueue, ordered="false"; |
| |
| // 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(Address); |
| void allocate(Address); |
| void deallocate(Address); |
| bool isPresent(Address); |
| } |
| |
| |
| // STRUCTURES |
| |
| TBETable TBEs, template_hack="<L1Cache_TBE>"; |
| |
| // PROTOTYPES |
| void set_cache_entry(AbstractCacheEntry a); |
| void unset_cache_entry(); |
| void set_tbe(TBE b); |
| void unset_tbe(); |
| |
| Entry getCacheEntry(Address 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, Address 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, Address addr, State state) { |
| |
| if (is_valid(tbe)) { |
| tbe.TBEState := state; |
| } |
| |
| if (is_valid(cache_entry)) { |
| cache_entry.CacheState := state; |
| } |
| } |
| |
| AccessPermission getAccessPermission(Address 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, Address addr, State state) { |
| if (is_valid(cache_entry)) { |
| cache_entry.changePermission(L1Cache_State_to_permission(state)); |
| } |
| } |
| |
| DataBlock getDataBlock(Address addr), return_by_ref="yes" { |
| return getCacheEntry(addr).DataBlk; |
| } |
| |
| GenericMachineType getNondirectHitMachType(MachineID sender) { |
| if (machineIDToMachineType(sender) == MachineType:L1Cache) { |
| // |
| // NOTE direct local hits should not call this |
| // |
| return GenericMachineType:L1Cache_wCC; |
| } else { |
| return ConvertMachToGenericMach(machineIDToMachineType(sender)); |
| } |
| } |
| |
| |
| // NETWORK PORTS |
| |
| out_port(requestNetwork_out, RequestMsg, requestFromCache); |
| out_port(responseNetwork_out, ResponseMsg, responseFromCache); |
| |
| in_port(forwardRequestNetwork_in, RequestMsg, forwardToCache) { |
| if (forwardRequestNetwork_in.isReady()) { |
| peek(forwardRequestNetwork_in, RequestMsg, block_on="Address") { |
| |
| Entry cache_entry := getCacheEntry(in_msg.Address); |
| TBE tbe := TBEs[in_msg.Address]; |
| |
| if (in_msg.Type == CoherenceRequestType:GETX) { |
| trigger(Event:Fwd_GETX, in_msg.Address, cache_entry, tbe); |
| } |
| else if (in_msg.Type == CoherenceRequestType:WB_ACK) { |
| trigger(Event:Writeback_Ack, in_msg.Address, cache_entry, tbe); |
| } |
| else if (in_msg.Type == CoherenceRequestType:WB_NACK) { |
| trigger(Event:Writeback_Nack, in_msg.Address, cache_entry, tbe); |
| } |
| else if (in_msg.Type == CoherenceRequestType:INV) { |
| trigger(Event:Inv, in_msg.Address, cache_entry, tbe); |
| } |
| else { |
| error("Unexpected message"); |
| } |
| } |
| } |
| } |
| |
| in_port(responseNetwork_in, ResponseMsg, responseToCache) { |
| if (responseNetwork_in.isReady()) { |
| peek(responseNetwork_in, ResponseMsg, block_on="Address") { |
| |
| Entry cache_entry := getCacheEntry(in_msg.Address); |
| TBE tbe := TBEs[in_msg.Address]; |
| |
| if (in_msg.Type == CoherenceResponseType:DATA) { |
| trigger(Event:Data, in_msg.Address, cache_entry, tbe); |
| } |
| else { |
| error("Unexpected message"); |
| } |
| } |
| } |
| } |
| |
| // Mandatory Queue |
| in_port(mandatoryQueue_in, RubyRequest, mandatoryQueue, desc="...") { |
| if (mandatoryQueue_in.isReady()) { |
| 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, latency=issue_latency) { |
| out_msg.Address := address; |
| out_msg.Type := CoherenceRequestType:GETX; |
| out_msg.Requestor := machineID; |
| out_msg.Destination.add(map_Address_to_Directory(address)); |
| out_msg.MessageSize := MessageSizeType:Control; |
| } |
| } |
| |
| action(b_issuePUT, "b", desc="Issue a PUT request") { |
| enqueue(requestNetwork_out, RequestMsg, latency=issue_latency) { |
| assert(is_valid(cache_entry)); |
| out_msg.Address := address; |
| out_msg.Type := CoherenceRequestType:PUTX; |
| out_msg.Requestor := machineID; |
| out_msg.Destination.add(map_Address_to_Directory(address)); |
| 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, latency=cache_response_latency) { |
| assert(is_valid(cache_entry)); |
| out_msg.Address := 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, latency=cache_response_latency) { |
| assert(is_valid(tbe)); |
| out_msg.Address := 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(); |
| } |
| |
| action(n_popResponseQueue, "n", desc="Pop the response queue") { |
| profileMsgDelay(1, responseNetwork_in.dequeue_getDelayCycles()); |
| } |
| |
| action(o_popForwardedRequestQueue, "o", desc="Pop the forwarded request queue") { |
| profileMsgDelay(2, forwardRequestNetwork_in.dequeue_getDelayCycles()); |
| } |
| |
| action(p_profileMiss, "p", desc="Profile cache miss") { |
| peek(mandatoryQueue_in, RubyRequest) { |
| cacheMemory.profileMiss(in_msg); |
| } |
| } |
| |
| action(r_load_hit, "r", desc="Notify sequencer the load completed.") { |
| assert(is_valid(cache_entry)); |
| DPRINTF(RubySlicc,"%s\n", cache_entry.DataBlk); |
| sequencer.readCallback(address, |
| GenericMachineType:L1Cache, |
| cache_entry.DataBlk); |
| } |
| |
| 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); |
| sequencer.readCallback(address, |
| getNondirectHitMachType(in_msg.Sender), |
| cache_entry.DataBlk); |
| } |
| } |
| |
| action(s_store_hit, "s", desc="Notify sequencer that store completed.") { |
| assert(is_valid(cache_entry)); |
| DPRINTF(RubySlicc,"%s\n", cache_entry.DataBlk); |
| sequencer.writeCallback(address, |
| GenericMachineType:L1Cache, |
| cache_entry.DataBlk); |
| } |
| |
| 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); |
| sequencer.writeCallback(address, |
| getNondirectHitMachType(in_msg.Sender), |
| cache_entry.DataBlk); |
| } |
| } |
| |
| 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(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}, {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; |
| m_popMandatoryQueue; |
| } |
| |
| transition(M, {Load, Ifetch}) { |
| r_load_hit; |
| 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; |
| o_popForwardedRequestQueue; |
| } |
| |
| transition(I, Replacement) { |
| h_deallocateL1CacheBlock; |
| } |
| |
| transition(M, {Replacement,Inv}, MI) { |
| v_allocateTBE; |
| b_issuePUT; |
| x_copyDataFromCacheToTBE; |
| 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; |
| } |
| } |
| |