d2cfa075150d47bccd6e4d078490a98111824093
[gem5.git] / src / mem / ruby / system / HTMSequencer.cc
1 /*
2 * Copyright (c) 2020 ARM Limited
3 * All rights reserved
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder. You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions are
16 * met: redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer;
18 * redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution;
21 * neither the name of the copyright holders nor the names of its
22 * contributors may be used to endorse or promote products derived from
23 * this software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37
38 #include "mem/ruby/system/HTMSequencer.hh"
39
40 #include "debug/HtmMem.hh"
41 #include "debug/RubyPort.hh"
42 #include "mem/ruby/slicc_interface/RubySlicc_Util.hh"
43 #include "sim/system.hh"
44
45 using namespace std;
46
47 HtmCacheFailure
48 HTMSequencer::htmRetCodeConversion(
49 const HtmFailedInCacheReason ruby_ret_code)
50 {
51 switch (ruby_ret_code) {
52 case HtmFailedInCacheReason_NO_FAIL:
53 return HtmCacheFailure::NO_FAIL;
54 case HtmFailedInCacheReason_FAIL_SELF:
55 return HtmCacheFailure::FAIL_SELF;
56 case HtmFailedInCacheReason_FAIL_REMOTE:
57 return HtmCacheFailure::FAIL_REMOTE;
58 case HtmFailedInCacheReason_FAIL_OTHER:
59 return HtmCacheFailure::FAIL_OTHER;
60 default:
61 panic("Invalid htm return code\n");
62 }
63 }
64
65 HTMSequencer *
66 RubyHTMSequencerParams::create()
67 {
68 return new HTMSequencer(this);
69 }
70
71 HTMSequencer::HTMSequencer(const RubyHTMSequencerParams *p)
72 : Sequencer(p)
73 {
74 m_htmstart_tick = 0;
75 m_htmstart_instruction = 0;
76 }
77
78 HTMSequencer::~HTMSequencer()
79 {
80 }
81
82 void
83 HTMSequencer::htmCallback(Addr address,
84 const HtmCallbackMode mode,
85 const HtmFailedInCacheReason htm_return_code)
86 {
87 // mode=0: HTM command
88 // mode=1: transaction failed - inform via LD
89 // mode=2: transaction failed - inform via ST
90
91 if (mode == HtmCallbackMode_HTM_CMD) {
92 SequencerRequest* request = nullptr;
93
94 assert(m_htmCmdRequestTable.size() > 0);
95
96 request = m_htmCmdRequestTable.front();
97 m_htmCmdRequestTable.pop_front();
98
99 assert(isHtmCmdRequest(request->m_type));
100
101 PacketPtr pkt = request->pkt;
102 delete request;
103
104 // valid responses have zero as the payload
105 uint8_t* dataptr = pkt->getPtr<uint8_t>();
106 memset(dataptr, 0, pkt->getSize());
107 *dataptr = (uint8_t) htm_return_code;
108
109 // record stats
110 if (htm_return_code == HtmFailedInCacheReason_NO_FAIL) {
111 if (pkt->req->isHTMStart()) {
112 m_htmstart_tick = pkt->req->time();
113 m_htmstart_instruction = pkt->req->getInstCount();
114 DPRINTF(HtmMem, "htmStart - htmUid=%u\n",
115 pkt->getHtmTransactionUid());
116 } else if (pkt->req->isHTMCommit()) {
117 Tick transaction_ticks = pkt->req->time() - m_htmstart_tick;
118 Cycles transaction_cycles = ticksToCycles(transaction_ticks);
119 m_htm_transaction_cycles.sample(transaction_cycles);
120 m_htmstart_tick = 0;
121 Counter transaction_instructions =
122 pkt->req->getInstCount() - m_htmstart_instruction;
123 m_htm_transaction_instructions.sample(
124 transaction_instructions);
125 m_htmstart_instruction = 0;
126 DPRINTF(HtmMem, "htmCommit - htmUid=%u\n",
127 pkt->getHtmTransactionUid());
128 } else if (pkt->req->isHTMAbort()) {
129 HtmFailureFaultCause cause = pkt->req->getHtmAbortCause();
130 assert(cause != HtmFailureFaultCause::INVALID);
131 auto cause_idx = static_cast<int>(cause);
132 m_htm_transaction_abort_cause[cause_idx]++;
133 DPRINTF(HtmMem, "htmAbort - reason=%s - htmUid=%u\n",
134 htmFailureToStr(cause),
135 pkt->getHtmTransactionUid());
136 }
137 } else {
138 DPRINTF(HtmMem, "HTM_CMD: fail - htmUid=%u\n",
139 pkt->getHtmTransactionUid());
140 }
141
142 rubyHtmCallback(pkt, htm_return_code);
143 testDrainComplete();
144 } else if (mode == HtmCallbackMode_LD_FAIL ||
145 mode == HtmCallbackMode_ST_FAIL) {
146 // transaction failed
147 assert(address == makeLineAddress(address));
148 assert(m_RequestTable.find(address) != m_RequestTable.end());
149
150 auto &seq_req_list = m_RequestTable[address];
151 while (!seq_req_list.empty()) {
152 SequencerRequest &request = seq_req_list.front();
153
154 PacketPtr pkt = request.pkt;
155 markRemoved();
156
157 // TODO - atomics
158
159 // store conditionals should indicate failure
160 if (request.m_type == RubyRequestType_Store_Conditional) {
161 pkt->req->setExtraData(0);
162 }
163
164 DPRINTF(HtmMem, "%s_FAIL: size=%d - "
165 "addr=0x%lx - htmUid=%d\n",
166 (mode == HtmCallbackMode_LD_FAIL) ? "LD" : "ST",
167 pkt->getSize(),
168 address, pkt->getHtmTransactionUid());
169
170 rubyHtmCallback(pkt, htm_return_code);
171 testDrainComplete();
172 pkt = nullptr;
173 seq_req_list.pop_front();
174 }
175 // free all outstanding requests corresponding to this address
176 if (seq_req_list.empty()) {
177 m_RequestTable.erase(address);
178 }
179 } else {
180 panic("unrecognised HTM callback mode\n");
181 }
182 }
183
184 void
185 HTMSequencer::regStats()
186 {
187 Sequencer::regStats();
188
189 // hardware transactional memory
190 m_htm_transaction_cycles
191 .init(10)
192 .name(name() + ".htm_transaction_cycles")
193 .desc("number of cycles spent in an outer transaction")
194 .flags(Stats::pdf | Stats::dist | Stats::nozero | Stats::nonan)
195 ;
196 m_htm_transaction_instructions
197 .init(10)
198 .name(name() + ".htm_transaction_instructions")
199 .desc("number of instructions spent in an outer transaction")
200 .flags(Stats::pdf | Stats::dist | Stats::nozero | Stats::nonan)
201 ;
202 auto num_causes = static_cast<int>(HtmFailureFaultCause::NUM_CAUSES);
203 m_htm_transaction_abort_cause
204 .init(num_causes)
205 .name(name() + ".htm_transaction_abort_cause")
206 .desc("cause of htm transaction abort")
207 .flags(Stats::total | Stats::pdf | Stats::dist | Stats::nozero)
208 ;
209
210 for (unsigned cause_idx = 0; cause_idx < num_causes; ++cause_idx) {
211 m_htm_transaction_abort_cause.subname(
212 cause_idx,
213 htmFailureToStr(HtmFailureFaultCause(cause_idx)));
214 }
215 }
216
217 void
218 HTMSequencer::rubyHtmCallback(PacketPtr pkt,
219 const HtmFailedInCacheReason htm_return_code)
220 {
221 // The packet was destined for memory and has not yet been turned
222 // into a response
223 assert(system->isMemAddr(pkt->getAddr()) || system->isDeviceMemAddr(pkt));
224 assert(pkt->isRequest());
225
226 // First retrieve the request port from the sender State
227 RubyPort::SenderState *senderState =
228 safe_cast<RubyPort::SenderState *>(pkt->popSenderState());
229
230 MemSlavePort *port = safe_cast<MemSlavePort*>(senderState->port);
231 assert(port != nullptr);
232 delete senderState;
233
234 //port->htmCallback(pkt, htm_return_code);
235 DPRINTF(HtmMem, "HTM callback: start=%d, commit=%d, "
236 "cancel=%d, rc=%d\n",
237 pkt->req->isHTMStart(), pkt->req->isHTMCommit(),
238 pkt->req->isHTMCancel(), htm_return_code);
239
240 // turn packet around to go back to requester if response expected
241 if (pkt->needsResponse()) {
242 DPRINTF(RubyPort, "Sending packet back over port\n");
243 pkt->makeHtmTransactionalReqResponse(
244 htmRetCodeConversion(htm_return_code));
245 port->schedTimingResp(pkt, curTick());
246 } else {
247 delete pkt;
248 }
249
250 trySendRetries();
251 }
252
253 void
254 HTMSequencer::wakeup()
255 {
256 Sequencer::wakeup();
257
258 // Check for deadlock of any of the requests
259 Cycles current_time = curCycle();
260
261 // hardware transactional memory commands
262 std::deque<SequencerRequest*>::iterator htm =
263 m_htmCmdRequestTable.begin();
264 std::deque<SequencerRequest*>::iterator htm_end =
265 m_htmCmdRequestTable.end();
266
267 for (; htm != htm_end; ++htm) {
268 SequencerRequest* request = *htm;
269 if (current_time - request->issue_time < m_deadlock_threshold)
270 continue;
271
272 panic("Possible Deadlock detected. Aborting!\n"
273 "version: %d m_htmCmdRequestTable: %d "
274 "current time: %u issue_time: %d difference: %d\n",
275 m_version, m_htmCmdRequestTable.size(),
276 current_time * clockPeriod(),
277 request->issue_time * clockPeriod(),
278 (current_time * clockPeriod()) -
279 (request->issue_time * clockPeriod()));
280 }
281 }
282
283 bool
284 HTMSequencer::empty() const
285 {
286 return Sequencer::empty() && m_htmCmdRequestTable.empty();
287 }
288
289 template <class VALUE>
290 std::ostream &
291 operator<<(ostream &out, const std::deque<VALUE> &queue)
292 {
293 auto i = queue.begin();
294 auto end = queue.end();
295
296 out << "[";
297 for (; i != end; ++i)
298 out << " " << *i;
299 out << " ]";
300
301 return out;
302 }
303
304 void
305 HTMSequencer::print(ostream& out) const
306 {
307 Sequencer::print(out);
308
309 out << "+ [HTMSequencer: " << m_version
310 << ", htm cmd request table: " << m_htmCmdRequestTable
311 << "]";
312 }
313
314 // Insert the request in the request table. Return RequestStatus_Aliased
315 // if the entry was already present.
316 RequestStatus
317 HTMSequencer::insertRequest(PacketPtr pkt, RubyRequestType primary_type,
318 RubyRequestType secondary_type)
319 {
320 if (isHtmCmdRequest(primary_type)) {
321 // for the moment, allow just one HTM cmd into the cache controller.
322 // Later this can be adjusted for optimization, e.g.
323 // back-to-back HTM_Starts.
324 if ((m_htmCmdRequestTable.size() > 0) && !pkt->req->isHTMAbort())
325 return RequestStatus_BufferFull;
326
327 // insert request into HtmCmd queue
328 SequencerRequest* htmReq =
329 new SequencerRequest(pkt, primary_type, secondary_type,
330 curCycle());
331 assert(htmReq);
332 m_htmCmdRequestTable.push_back(htmReq);
333 return RequestStatus_Ready;
334 } else {
335 return Sequencer::insertRequest(pkt, primary_type, secondary_type);
336 }
337 }