Tuan Ta has uploaded this change for review. (
https://gem5-review.googlesource.com/c/public/gem5/+/16769
Change subject: ruby,protocol,configs: Ensure lr/sc forward progress on
MESI_Two_Level
......................................................................
ruby,protocol,configs: Ensure lr/sc forward progress on MESI_Two_Level
This commit addresses LL/SC livelock issue in ruby MESI_Two_Level protocol.
As
per RISC-V ISA, restricted lr/sc sequences are guaranteed to have forward
progress. We implement this property by "locking" a cacheline obtained by LL
instruction for bounded amount of cycles.
Change-Id: I243d9f00789cc398753b2b42a802fbc349c29e73
---
M configs/ruby/MESI_Two_Level.py
M src/mem/protocol/MESI_Two_Level-L1cache.sm
M src/mem/protocol/MESI_Two_Level-msg.sm
M src/mem/protocol/RubySlicc_Types.sm
M src/mem/ruby/slicc_interface/RubyRequest.hh
M src/mem/ruby/system/Sequencer.cc
M src/mem/ruby/system/Sequencer.hh
7 files changed, 216 insertions(+), 43 deletions(-)
diff --git a/configs/ruby/MESI_Two_Level.py b/configs/ruby/MESI_Two_Level.py
index 27ef9c8..2adf271 100644
--- a/configs/ruby/MESI_Two_Level.py
+++ b/configs/ruby/MESI_Two_Level.py
@@ -132,6 +132,7 @@
l1_cntrl.responseToL1Cache = MessageBuffer()
l1_cntrl.responseToL1Cache.slave = ruby_system.network.master
+ l1_cntrl.triggerQueue = MessageBuffer()
l2_index_start = block_size_bits + l2_bits
diff --git a/src/mem/protocol/MESI_Two_Level-L1cache.sm
b/src/mem/protocol/MESI_Two_Level-L1cache.sm
index 87684ce..57eeef6 100644
--- a/src/mem/protocol/MESI_Two_Level-L1cache.sm
+++ b/src/mem/protocol/MESI_Two_Level-L1cache.sm
@@ -37,6 +37,7 @@
Cycles to_l2_latency := 1;
bool send_evictions;
bool enable_prefetch := "False";
+ Cycles llsc_locked_duration := 128;
// Message Queues
// From this node's L1 cache TO the network
@@ -67,6 +68,9 @@
// Buffer for requests generated by the processor core.
MessageBuffer * mandatoryQueue;
+
+ // Buffer for unlock
+ MessageBuffer * triggerQueue;
{
// STATES
state_declaration(State, desc="Cache states", default="L1Cache_State_I")
{
@@ -76,11 +80,14 @@
S, AccessPermission:Read_Only, desc="a L1 cache entry Shared";
E, AccessPermission:Read_Only, desc="a L1 cache entry Exclusive";
M, AccessPermission:Read_Write, desc="a L1 cache entry Modified",
format="!b";
+ L, AccessPermission:Read_Write, desc="a L1 cache entry Modified and
locked";
// Transient States
IS, AccessPermission:Busy, desc="L1 idle, issued GETS, have not seen
response yet";
IM, AccessPermission:Busy, desc="L1 idle, issued GETX, have not seen
response yet";
+ IL, AccessPermission:Busy, desc="L1 idle, issued GETX, have not seen
response yet, will be locked";
SM, AccessPermission:Read_Only, desc="L1 idle, issued GETX, have not
seen response yet";
+ SL, AccessPermission:Read_Only, desc="L1 idle, issued GETX, have not
seen response yet, will be locked";
IS_I, AccessPermission:Busy, desc="L1 idle, issued GETS, saw Inv
before data because directory doesn't block on GETS hit";
M_I, AccessPermission:Busy, desc="L1 replacing, waiting for ACK";
@@ -99,6 +106,7 @@
Load, desc="Load request from the home processor";
Ifetch, desc="I-fetch request from the home processor";
Store, desc="Store request from the home processor";
+ LL, desc="Load-Linked";
Inv, desc="Invalidate request from L2 bank";
@@ -124,6 +132,9 @@
PF_Load, desc="load request from prefetcher";
PF_Ifetch, desc="instruction fetch request from prefetcher";
PF_Store, desc="exclusive load request from prefetcher";
+
+ // unlock
+ Unlock, desc="Unlock locked cacheline";
}
// TYPES
@@ -164,6 +175,7 @@
void set_tbe(TBE a);
void unset_tbe();
void wakeUpBuffers(Addr a);
+ void wakeUpAllBuffers();
void profileMsgDelay(int virtualNetworkType, Cycles c);
// inclusive cache returns L1 entries only
@@ -258,13 +270,20 @@
}
}
- Event mandatory_request_type_to_event(RubyRequestType type) {
- if (type == RubyRequestType:LD) {
+ Event mandatory_request_type_to_event(RubyRequestType stype,
RubyRequestType ptype) {
+ if (stype == RubyRequestType:LD) {
return Event:Load;
- } else if (type == RubyRequestType:IFETCH) {
+ } else if (stype == RubyRequestType:IFETCH) {
return Event:Ifetch;
- } else if ((type == RubyRequestType:ST) || (type ==
RubyRequestType:ATOMIC)) {
- return Event:Store;
+ } else if ((stype == RubyRequestType:ST) ||
+ (stype == RubyRequestType:ATOMIC) ||
+ (stype == RubyRequestType:ATOMIC_NO_RETURN) ||
+ (stype == RubyRequestType:ATOMIC_RETURN)) {
+ if (ptype == RubyRequestType:Load_Linked) {
+ return Event:LL;
+ } else {
+ return Event:Store;
+ }
} else {
error("Invalid RubyRequestType");
}
@@ -291,7 +310,18 @@
out_port(responseL1Network_out, ResponseMsg, responseFromL1Cache);
out_port(unblockNetwork_out, ResponseMsg, unblockFromL1Cache);
out_port(optionalQueue_out, RubyRequest, optionalQueue);
+ out_port(triggerQueue_out, RequestMsg, triggerQueue);
+ // Trigger Queue for unlocking cachelines
+ in_port(triggerQueue_in, RequestMsg, triggerQueue) {
+ if (triggerQueue_in.isReady(clockEdge())) {
+ peek(triggerQueue_in, RequestMsg) {
+ Entry cache_entry := getCacheEntry(in_msg.addr);
+ TBE tbe := TBEs[in_msg.addr];
+ trigger(Event:Unlock, in_msg.addr, cache_entry, tbe);
+ }
+ }
+ }
// Prefetch queue between the controller and the prefetcher
// As per Spracklen et al. (HPCA 2005), the prefetch queue should be
@@ -453,8 +483,8 @@
Entry L1Icache_entry := getL1ICacheEntry(in_msg.LineAddress);
if (is_valid(L1Icache_entry)) {
// The tag matches for the L1, so the L1 asks the L2 for it.
- trigger(mandatory_request_type_to_event(in_msg.Type),
in_msg.LineAddress,
- L1Icache_entry, TBEs[in_msg.LineAddress]);
+ trigger(mandatory_request_type_to_event(in_msg.Type,
in_msg.PrimaryType),
+ in_msg.LineAddress, L1Icache_entry,
TBEs[in_msg.LineAddress]);
} else {
// Check to see if it is in the OTHER L1
@@ -468,8 +498,8 @@
if (L1Icache.cacheAvail(in_msg.LineAddress)) {
// L1 does't have the line, but we have space for it
// in the L1 so let's see if the L2 has it.
- trigger(mandatory_request_type_to_event(in_msg.Type),
in_msg.LineAddress,
- L1Icache_entry, TBEs[in_msg.LineAddress]);
+ trigger(mandatory_request_type_to_event(in_msg.Type,
in_msg.PrimaryType),
+ in_msg.LineAddress, L1Icache_entry,
TBEs[in_msg.LineAddress]);
} else {
// No room in the L1, so we need to make room in the L1
trigger(Event:L1_Replacement,
L1Icache.cacheProbe(in_msg.LineAddress),
@@ -483,8 +513,8 @@
Entry L1Dcache_entry := getL1DCacheEntry(in_msg.LineAddress);
if (is_valid(L1Dcache_entry)) {
// The tag matches for the L1, so the L1 ask the L2 for it
- trigger(mandatory_request_type_to_event(in_msg.Type),
in_msg.LineAddress,
- L1Dcache_entry, TBEs[in_msg.LineAddress]);
+ trigger(mandatory_request_type_to_event(in_msg.Type,
in_msg.PrimaryType),
+ in_msg.LineAddress, L1Dcache_entry,
TBEs[in_msg.LineAddress]);
} else {
// Check to see if it is in the OTHER L1
@@ -498,8 +528,8 @@
if (L1Dcache.cacheAvail(in_msg.LineAddress)) {
// L1 does't have the line, but we have space for it
// in the L1 let's see if the L2 has it.
- trigger(mandatory_request_type_to_event(in_msg.Type),
in_msg.LineAddress,
- L1Dcache_entry, TBEs[in_msg.LineAddress]);
+ trigger(mandatory_request_type_to_event(in_msg.Type,
in_msg.PrimaryType),
+ in_msg.LineAddress, L1Dcache_entry,
TBEs[in_msg.LineAddress]);
} else {
// No room in the L1, so we need to make room in the L1
trigger(Event:L1_Replacement,
L1Dcache.cacheProbe(in_msg.LineAddress),
@@ -936,10 +966,22 @@
stall_and_wait(optionalQueue_in, address);
}
+ action(z_stallAndWaitRequestQueue, "\zr", desc="recycle L1 request
queue") {
+ stall_and_wait(requestL1Network_in, address);
+ }
+
+ action(z_stall, "zs", desc="Stall the incoming request") {
+ // do nothing
+ }
+
action(kd_wakeUpDependents, "kd", desc="wake-up dependents") {
wakeUpBuffers(address);
}
+ action(kd_wakeUpAllDependents, "kda", desc="wake-up all dependents") {
+ wakeUpAllBuffers();
+ }
+
action(uu_profileInstMiss, "\uim", desc="Profile the demand miss") {
++L1Icache.demand_misses;
}
@@ -989,17 +1031,31 @@
cache_entry.isPrefetch := true;
}
+ action(su_scheduleUnlock, "su", desc="Schdule an unlock event") {
+ assert(is_valid(cache_entry));
+ enqueue(triggerQueue_out, RequestMsg, llsc_locked_duration) {
+ out_msg.addr := address;
+ out_msg.Type := CoherenceRequestType:UNLOCK;
+ out_msg.MessageSize := MessageSizeType:Control;
+ DPRINTF(RubySlicc, "schedule to unlock line %#x at %s\n", address,
+ ticksToCycles(clockEdge()) + llsc_locked_duration);
+ }
+ }
+
+ action(pt_popTriggerQueue, "pT", desc="Pop the trigger queue") {
+ triggerQueue_in.dequeue(clockEdge());
+ }
//*****************************************************
// TRANSITIONS
//*****************************************************
// Transitions for Load/Store/Replacement/WriteBack from transient states
- transition({IS, IM, IS_I, M_I, SM, SINK_WB_ACK}, {Load, Ifetch, Store,
L1_Replacement}) {
+ transition({IS, IM, IS_I, M_I, SM, SINK_WB_ACK}, {Load, Ifetch, Store,
LL, L1_Replacement}) {
z_stallAndWaitMandatoryQueue;
}
- transition({PF_IS, PF_IS_I}, {Store, L1_Replacement}) {
+ transition({PF_IS, PF_IS_I}, {Store, L1_Replacement, LL}) {
z_stallAndWaitMandatoryQueue;
}
@@ -1088,6 +1144,15 @@
k_popMandatoryQueue;
}
+ transition({NP,I}, LL, IL) {
+ oo_allocateL1DCacheBlock;
+ i_allocateTBE;
+ b_issueGETX;
+ uu_profileDataMiss;
+ po_observeMiss;
+ k_popMandatoryQueue;
+ }
+
transition({NP,I}, PF_Store, PF_IM) {
oo_allocateL1DCacheBlock;
i_allocateTBE;
@@ -1101,26 +1166,38 @@
k_popMandatoryQueue;
}
+ transition(PF_IM, LL, IL) {
+ uu_profileDataMiss;
+ ppm_observePfMiss;
+ k_popMandatoryQueue;
+ }
+
transition(PF_SM, Store, SM) {
uu_profileDataMiss;
ppm_observePfMiss;
k_popMandatoryQueue;
}
+ transition(PF_SM, LL, SL) {
+ uu_profileDataMiss;
+ ppm_observePfMiss;
+ k_popMandatoryQueue;
+ }
+
transition({NP, I}, Inv) {
fi_sendInvAck;
l_popRequestQueue;
}
// Transitions from Shared
- transition({S,E,M}, Load) {
+ transition({S,E,M,L}, Load) {
h_load_hit;
uu_profileDataHit;
po_observeHit;
k_popMandatoryQueue;
}
- transition({S,E,M}, Ifetch) {
+ transition({S,E,M,L}, Ifetch) {
h_ifetch_hit;
uu_profileInstHit;
po_observeHit;
@@ -1134,6 +1211,13 @@
k_popMandatoryQueue;
}
+ transition(S, LL, SL) {
+ i_allocateTBE;
+ c_issueUPGRADE;
+ uu_profileDataMiss;
+ k_popMandatoryQueue;
+ }
+
transition(S, {L1_Replacement, PF_L1_Replacement}, I) {
forward_eviction_to_cpu;
ff_deallocateL1CacheBlock;
@@ -1146,7 +1230,6 @@
}
// Transitions from Exclusive
-
transition({E,M}, Store, M) {
hh_store_hit;
uu_profileDataHit;
@@ -1154,6 +1237,13 @@
k_popMandatoryQueue;
}
+ transition({E,M}, LL, L) {
+ su_scheduleUnlock;
+ hh_store_hit;
+ uu_profileDataHit;
+ k_popMandatoryQueue;
+ }
+
transition(E, {L1_Replacement, PF_L1_Replacement}, M_I) {
// silent E replacement??
forward_eviction_to_cpu;
@@ -1182,7 +1272,6 @@
}
// Transitions from Modified
-
transition(M, {L1_Replacement, PF_L1_Replacement}, M_I) {
forward_eviction_to_cpu;
i_allocateTBE;
@@ -1230,6 +1319,26 @@
l_popRequestQueue;
}
+ // Transitions from Locked
+ transition(L, {Store, LL}) {
+ hh_store_hit;
+ uu_profileDataHit;
+ k_popMandatoryQueue;
+ }
+
+ transition(L, Unlock, M) {
+ pt_popTriggerQueue;
+ kd_wakeUpAllDependents;
+ }
+
+ transition(L, {Fwd_GETX, Fwd_GETS, Fwd_GET_INSTR, Inv}) {
+ z_stallAndWaitRequestQueue;
+ }
+
+ transition(L, L1_Replacement) {
+ z_stall;
+ }
+
// Transitions from IS
transition({IS, IS_I}, Inv, IS_I) {
fi_sendInvAck;
@@ -1342,7 +1451,7 @@
}
// Transitions from IM
- transition(IM, Inv, IM) {
+ transition({IM, IL}, Inv) {
fi_sendInvAck;
l_popRequestQueue;
}
@@ -1382,7 +1491,24 @@
kd_wakeUpDependents;
}
- // transitions from SM
+ // Transitions from IL
+ transition(IL, Data, SL) {
+ u_writeDataToL1Cache;
+ q_updateAckCount;
+ o_popIncomingResponseQueue;
+ }
+
+ transition(IL, Data_all_Acks, L) {
+ u_writeDataToL1Cache;
+ hhx_store_hit;
+ jj_sendExclusiveUnblock;
+ s_deallocateTBE;
+ o_popIncomingResponseQueue;
+ su_scheduleUnlock;
+ kd_wakeUpDependents;
+ }
+
+ // Transitions from SM
transition(SM, Inv, IM) {
forward_eviction_to_cpu;
fi_sendInvAck;
@@ -1390,7 +1516,7 @@
l_popRequestQueue;
}
- transition({SM, IM, PF_SM, PF_IM}, Ack) {
+ transition({SM, IM, PF_SM, PF_IM, SL, IL}, Ack) {
q_updateAckCount;
o_popIncomingResponseQueue;
}
@@ -1411,6 +1537,24 @@
kd_wakeUpDependents;
}
+ // Transitions from SL
+ transition(SL, Inv, IL) {
+ forward_eviction_to_cpu;
+ fi_sendInvAck;
+ dg_invalidate_sc;
+ l_popRequestQueue;
+ }
+
+ transition(SL, Ack_all, L) {
+ jj_sendExclusiveUnblock;
+ hhx_store_hit;
+ s_deallocateTBE;
+ o_popIncomingResponseQueue;
+ su_scheduleUnlock;
+ kd_wakeUpDependents;
+ }
+
+ // Transitions from SINK_WB_ACK
transition(SINK_WB_ACK, Inv){
fi_sendInvAck;
l_popRequestQueue;
diff --git a/src/mem/protocol/MESI_Two_Level-msg.sm
b/src/mem/protocol/MESI_Two_Level-msg.sm
index 738019e..e14015d 100644
--- a/src/mem/protocol/MESI_Two_Level-msg.sm
+++ b/src/mem/protocol/MESI_Two_Level-msg.sm
@@ -39,8 +39,10 @@
WB_ACK, desc="Writeback ack";
- DMA_READ, desc="DMA Read";
+ DMA_READ, desc="DMA Read";
DMA_WRITE, desc="DMA Write";
+
+ UNLOCK, desc="Unlock cache line";
}
// CoherenceResponseType
diff --git a/src/mem/protocol/RubySlicc_Types.sm
b/src/mem/protocol/RubySlicc_Types.sm
index 27a045d..6a699fb 100644
--- a/src/mem/protocol/RubySlicc_Types.sm
+++ b/src/mem/protocol/RubySlicc_Types.sm
@@ -158,20 +158,21 @@
}
structure(RubyRequest, desc="...", interface="Message", external="yes") {
- Addr LineAddress, desc="Line address for this request";
- Addr PhysicalAddress, desc="Physical address for this request";
- RubyRequestType Type, desc="Type of request (LD, ST, etc)";
- Addr ProgramCounter, desc="Program counter of the instruction that
caused the miss";
- RubyAccessMode AccessMode, desc="user/supervisor access type";
- int Size, desc="size in bytes of access";
- PrefetchBit Prefetch, desc="Is this a prefetch request";
- int contextId, desc="this goes away but must be replace with
Nilay";
- WriteMask writeMask, desc="Writethrough mask";
- DataBlock WTData, desc="Writethrough data block";
- int wfid, desc="Writethrough wavefront";
- HSAScope scope, desc="HSA scope";
- HSASegment segment, desc="HSA segment";
- PacketPtr pkt, desc="Packet associated with this request";
+ Addr LineAddress, desc="Line address for this request";
+ Addr PhysicalAddress, desc="Physical address for this request";
+ RubyRequestType Type, desc="Type of request (LD, ST, etc)";
+ RubyRequestType PrimaryType, desc="Type of primary request";
+ Addr ProgramCounter, desc="Program counter of the instruction
that caused the miss";
+ RubyAccessMode AccessMode, desc="user/supervisor access type";
+ int Size, desc="size in bytes of access";
+ PrefetchBit Prefetch, desc="Is this a prefetch request";
+ int contextId, desc="this goes away but must be replace
with Nilay";
+ WriteMask writeMask, desc="Writethrough mask";
+ DataBlock WTData, desc="Writethrough data block";
+ int wfid, desc="Writethrough wavefront";
+ HSAScope scope, desc="HSA scope";
+ HSASegment segment, desc="HSA segment";
+ PacketPtr pkt, desc="Packet associated with this request";
}
structure(AbstractEntry, primitive="yes", external = "yes") {
diff --git a/src/mem/ruby/slicc_interface/RubyRequest.hh
b/src/mem/ruby/slicc_interface/RubyRequest.hh
index 6c84f38..3894749 100644
--- a/src/mem/ruby/slicc_interface/RubyRequest.hh
+++ b/src/mem/ruby/slicc_interface/RubyRequest.hh
@@ -48,6 +48,7 @@
Addr m_PhysicalAddress;
Addr m_LineAddress;
RubyRequestType m_Type;
+ RubyRequestType m_PrimaryType;
Addr m_ProgramCounter;
RubyAccessMode m_AccessMode;
int m_Size;
@@ -61,7 +62,6 @@
HSAScope m_scope;
HSASegment m_segment;
-
RubyRequest(Tick curTime, uint64_t _paddr, uint8_t* _data, int _len,
uint64_t _pc, RubyRequestType _type, RubyAccessMode _access_mode,
PacketPtr _pkt, PrefetchBit _pb = PrefetchBit_No,
@@ -85,6 +85,29 @@
}
RubyRequest(Tick curTime, uint64_t _paddr, uint8_t* _data, int _len,
+ uint64_t _pc, RubyRequestType _type, RubyRequestType _ptype,
+ RubyAccessMode _access_mode, PacketPtr _pkt,
+ PrefetchBit _pb = PrefetchBit_No, ContextID _proc_id = 100,
+ ContextID _core_id = 99, HSAScope _scope = HSAScope_UNSPECIFIED,
+ HSASegment _segment = HSASegment_GLOBAL)
+ : Message(curTime),
+ m_PhysicalAddress(_paddr),
+ m_Type(_type),
+ m_PrimaryType(_ptype),
+ m_ProgramCounter(_pc),
+ m_AccessMode(_access_mode),
+ m_Size(_len),
+ m_Prefetch(_pb),
+ data(_data),
+ m_pkt(_pkt),
+ m_contextId(_core_id),
+ m_scope(_scope),
+ m_segment(_segment)
+ {
+ m_LineAddress = makeLineAddress(m_PhysicalAddress);
+ }
+
+ RubyRequest(Tick curTime, uint64_t _paddr, uint8_t* _data, int _len,
uint64_t _pc, RubyRequestType _type,
RubyAccessMode _access_mode, PacketPtr _pkt, PrefetchBit _pb,
unsigned _proc_id, unsigned _core_id,
diff --git a/src/mem/ruby/system/Sequencer.cc
b/src/mem/ruby/system/Sequencer.cc
index a884981..d6830d9 100644
--- a/src/mem/ruby/system/Sequencer.cc
+++ b/src/mem/ruby/system/Sequencer.cc
@@ -640,14 +640,15 @@
if (status != RequestStatus_Ready)
return status;
- issueRequest(pkt, secondary_type);
+ issueRequest(pkt, secondary_type, primary_type);
// TODO: issue hardware prefetches here
return RequestStatus_Issued;
}
void
-Sequencer::issueRequest(PacketPtr pkt, RubyRequestType secondary_type)
+Sequencer::issueRequest(PacketPtr pkt, RubyRequestType secondary_type,
+ RubyRequestType primary_type)
{
assert(pkt != NULL);
ContextID proc_id = pkt->req->hasContextId() ?
@@ -668,8 +669,8 @@
pkt->isFlush() ?
nullptr : pkt->getPtr<uint8_t>(),
pkt->getSize(), pc, secondary_type,
- RubyAccessMode_Supervisor, pkt,
- PrefetchBit_No, proc_id, core_id);
+ primary_type,
RubyAccessMode_Supervisor,
+ pkt, PrefetchBit_No, proc_id,
core_id);
DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %#x %s\n",
curTick(), m_version, "Seq", "Begin", "", "",
diff --git a/src/mem/ruby/system/Sequencer.hh
b/src/mem/ruby/system/Sequencer.hh
index fcfa8ad..4a5b9ea 100644
--- a/src/mem/ruby/system/Sequencer.hh
+++ b/src/mem/ruby/system/Sequencer.hh
@@ -149,7 +149,8 @@
{ return m_IncompleteTimes[t]; }
private:
- void issueRequest(PacketPtr pkt, RubyRequestType type);
+ void issueRequest(PacketPtr pkt, RubyRequestType secondary_type,
+ RubyRequestType primary_type);
void hitCallback(SequencerRequest* request, DataBlock& data,
bool llscSuccess,
--
To view, visit https://gem5-review.googlesource.com/c/public/gem5/+/16769
To unsubscribe, or for help writing mail filters, visit
https://gem5-review.googlesource.com/settings
Gerrit-Project: public/gem5
Gerrit-Branch: master
Gerrit-Change-Id: I243d9f00789cc398753b2b42a802fbc349c29e73
Gerrit-Change-Number: 16769
Gerrit-PatchSet: 1
Gerrit-Owner: Tuan Ta <[email protected]>
Gerrit-MessageType: newchange
_______________________________________________
gem5-dev mailing list
[email protected]
http://m5sim.org/mailman/listinfo/gem5-dev