a317764577
This patch is imported from reviewboard patch 2551 by Nilay. This patch moves from a dynamically defined MachineType to a statically defined one. The need for this patch was felt since a dynamically defined type prevents us from having types for which no machine definition may exist. The following changes have been made: i. each machine definition now uses a type from the MachineType enumeration instead of any random identifier. This required changing the grammar and the *.sm files. ii. MachineType enumeration defined statically in RubySlicc_Exports.sm. * * * normal protocol fixes for nilay's parser machine type fix
527 lines
16 KiB
Plaintext
527 lines
16 KiB
Plaintext
/*
|
|
* Copyright (c) 2009-2012 Mark D. Hill and David A. Wood
|
|
* Copyright (c) 2010-2012 Advanced Micro Devices, Inc.
|
|
* 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(MachineType:L1Cache, "MI Example L1 Cache")
|
|
: Sequencer * sequencer;
|
|
CacheMemory * cacheMemory;
|
|
Cycles cache_response_latency := 12;
|
|
Cycles issue_latency := 2;
|
|
bool send_evictions;
|
|
|
|
// NETWORK BUFFERS
|
|
MessageBuffer * requestFromCache, network="To", virtual_network="2",
|
|
vnet_type="request";
|
|
MessageBuffer * responseFromCache, network="To", virtual_network="4",
|
|
vnet_type="response";
|
|
|
|
MessageBuffer * forwardToCache, network="From", virtual_network="3",
|
|
vnet_type="forward";
|
|
MessageBuffer * responseToCache, network="From", virtual_network="4",
|
|
vnet_type="response";
|
|
|
|
MessageBuffer * mandatoryQueue;
|
|
{
|
|
// 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
|
|
// 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(Addr);
|
|
void allocate(Addr);
|
|
void deallocate(Addr);
|
|
bool isPresent(Addr);
|
|
}
|
|
|
|
|
|
// STRUCTURES
|
|
TBETable TBEs, template="<L1Cache_TBE>", constructor="m_number_of_TBEs";
|
|
|
|
// PROTOTYPES
|
|
Tick clockEdge();
|
|
Cycles ticksToCycles(Tick t);
|
|
void set_cache_entry(AbstractCacheEntry a);
|
|
void unset_cache_entry();
|
|
void set_tbe(TBE b);
|
|
void unset_tbe();
|
|
void profileMsgDelay(int virtualNetworkType, Cycles b);
|
|
|
|
Entry getCacheEntry(Addr 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, Addr 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, Addr addr, State state) {
|
|
|
|
if (is_valid(tbe)) {
|
|
tbe.TBEState := state;
|
|
}
|
|
|
|
if (is_valid(cache_entry)) {
|
|
cache_entry.CacheState := state;
|
|
}
|
|
}
|
|
|
|
AccessPermission getAccessPermission(Addr 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, Addr addr, State state) {
|
|
if (is_valid(cache_entry)) {
|
|
cache_entry.changePermission(L1Cache_State_to_permission(state));
|
|
}
|
|
}
|
|
|
|
void functionalRead(Addr addr, Packet *pkt) {
|
|
TBE tbe := TBEs[addr];
|
|
if(is_valid(tbe)) {
|
|
testAndRead(addr, tbe.DataBlk, pkt);
|
|
} else {
|
|
testAndRead(addr, getCacheEntry(addr).DataBlk, pkt);
|
|
}
|
|
}
|
|
|
|
int functionalWrite(Addr addr, Packet *pkt) {
|
|
int num_functional_writes := 0;
|
|
|
|
TBE tbe := TBEs[addr];
|
|
if(is_valid(tbe)) {
|
|
num_functional_writes := num_functional_writes +
|
|
testAndWrite(addr, tbe.DataBlk, pkt);
|
|
return num_functional_writes;
|
|
}
|
|
|
|
num_functional_writes := num_functional_writes +
|
|
testAndWrite(addr, getCacheEntry(addr).DataBlk, pkt);
|
|
return num_functional_writes;
|
|
}
|
|
|
|
// NETWORK PORTS
|
|
|
|
out_port(requestNetwork_out, RequestMsg, requestFromCache);
|
|
out_port(responseNetwork_out, ResponseMsg, responseFromCache);
|
|
|
|
in_port(forwardRequestNetwork_in, RequestMsg, forwardToCache) {
|
|
if (forwardRequestNetwork_in.isReady(clockEdge())) {
|
|
peek(forwardRequestNetwork_in, RequestMsg, block_on="addr") {
|
|
|
|
Entry cache_entry := getCacheEntry(in_msg.addr);
|
|
TBE tbe := TBEs[in_msg.addr];
|
|
|
|
if (in_msg.Type == CoherenceRequestType:GETX) {
|
|
trigger(Event:Fwd_GETX, in_msg.addr, cache_entry, tbe);
|
|
}
|
|
else if (in_msg.Type == CoherenceRequestType:WB_ACK) {
|
|
trigger(Event:Writeback_Ack, in_msg.addr, cache_entry, tbe);
|
|
}
|
|
else if (in_msg.Type == CoherenceRequestType:WB_NACK) {
|
|
trigger(Event:Writeback_Nack, in_msg.addr, cache_entry, tbe);
|
|
}
|
|
else if (in_msg.Type == CoherenceRequestType:INV) {
|
|
trigger(Event:Inv, in_msg.addr, cache_entry, tbe);
|
|
}
|
|
else {
|
|
error("Unexpected message");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
in_port(responseNetwork_in, ResponseMsg, responseToCache) {
|
|
if (responseNetwork_in.isReady(clockEdge())) {
|
|
peek(responseNetwork_in, ResponseMsg, block_on="addr") {
|
|
|
|
Entry cache_entry := getCacheEntry(in_msg.addr);
|
|
TBE tbe := TBEs[in_msg.addr];
|
|
|
|
if (in_msg.Type == CoherenceResponseType:DATA) {
|
|
trigger(Event:Data, in_msg.addr, cache_entry, tbe);
|
|
}
|
|
else {
|
|
error("Unexpected message");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Mandatory Queue
|
|
in_port(mandatoryQueue_in, RubyRequest, mandatoryQueue, desc="...") {
|
|
if (mandatoryQueue_in.isReady(clockEdge())) {
|
|
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, issue_latency) {
|
|
out_msg.addr := 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, issue_latency) {
|
|
assert(is_valid(cache_entry));
|
|
out_msg.addr := 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, cache_response_latency) {
|
|
assert(is_valid(cache_entry));
|
|
out_msg.addr := 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, cache_response_latency) {
|
|
assert(is_valid(tbe));
|
|
out_msg.addr := 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(clockEdge());
|
|
}
|
|
|
|
action(n_popResponseQueue, "n", desc="Pop the response queue") {
|
|
Tick delay := responseNetwork_in.dequeue(clockEdge());
|
|
profileMsgDelay(1, ticksToCycles(delay));
|
|
}
|
|
|
|
action(o_popForwardedRequestQueue, "o", desc="Pop the forwarded request queue") {
|
|
Tick delay := forwardRequestNetwork_in.dequeue(clockEdge());
|
|
profileMsgDelay(2, ticksToCycles(delay));
|
|
}
|
|
|
|
action(p_profileMiss, "pi", desc="Profile cache miss") {
|
|
++cacheMemory.demand_misses;
|
|
}
|
|
|
|
action(p_profileHit, "ph", desc="Profile cache miss") {
|
|
++cacheMemory.demand_hits;
|
|
}
|
|
|
|
action(r_load_hit, "r", desc="Notify sequencer the load completed.") {
|
|
assert(is_valid(cache_entry));
|
|
DPRINTF(RubySlicc,"%s\n", cache_entry.DataBlk);
|
|
cacheMemory.setMRU(cache_entry);
|
|
sequencer.readCallback(address, cache_entry.DataBlk, false);
|
|
}
|
|
|
|
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);
|
|
cacheMemory.setMRU(cache_entry);
|
|
sequencer.readCallback(address, cache_entry.DataBlk, true,
|
|
machineIDToMachineType(in_msg.Sender));
|
|
}
|
|
}
|
|
|
|
action(s_store_hit, "s", desc="Notify sequencer that store completed.") {
|
|
assert(is_valid(cache_entry));
|
|
DPRINTF(RubySlicc,"%s\n", cache_entry.DataBlk);
|
|
cacheMemory.setMRU(cache_entry);
|
|
sequencer.writeCallback(address, cache_entry.DataBlk, false);
|
|
}
|
|
|
|
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);
|
|
cacheMemory.setMRU(cache_entry);
|
|
sequencer.writeCallback(address, cache_entry.DataBlk, true,
|
|
machineIDToMachineType(in_msg.Sender));
|
|
}
|
|
}
|
|
|
|
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(forward_eviction_to_cpu, "\cc", desc="sends eviction information to the processor") {
|
|
if (send_evictions) {
|
|
DPRINTF(RubySlicc, "Sending invalidation for %#x to the CPU\n", address);
|
|
sequencer.evictionCallback(address);
|
|
}
|
|
}
|
|
|
|
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, MII}, {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;
|
|
p_profileHit;
|
|
m_popMandatoryQueue;
|
|
}
|
|
|
|
transition(M, {Load, Ifetch}) {
|
|
r_load_hit;
|
|
p_profileHit;
|
|
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;
|
|
forward_eviction_to_cpu;
|
|
o_popForwardedRequestQueue;
|
|
}
|
|
|
|
transition(I, Replacement) {
|
|
h_deallocateL1CacheBlock;
|
|
}
|
|
|
|
transition(M, {Replacement,Inv}, MI) {
|
|
v_allocateTBE;
|
|
b_issuePUT;
|
|
x_copyDataFromCacheToTBE;
|
|
forward_eviction_to_cpu;
|
|
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;
|
|
}
|
|
}
|