2 * Copyright (c) 2020 ARM Limited
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.
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.
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.
38 #include "mem/ruby/system/HTMSequencer.hh"
40 #include "debug/HtmMem.hh"
41 #include "debug/RubyPort.hh"
42 #include "mem/ruby/slicc_interface/RubySlicc_Util.hh"
43 #include "sim/system.hh"
48 HTMSequencer::htmRetCodeConversion(
49 const HtmFailedInCacheReason ruby_ret_code
)
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
;
61 panic("Invalid htm return code\n");
66 RubyHTMSequencerParams::create()
68 return new HTMSequencer(this);
71 HTMSequencer::HTMSequencer(const RubyHTMSequencerParams
*p
)
75 m_htmstart_instruction
= 0;
78 HTMSequencer::~HTMSequencer()
83 HTMSequencer::htmCallback(Addr address
,
84 const HtmCallbackMode mode
,
85 const HtmFailedInCacheReason htm_return_code
)
87 // mode=0: HTM command
88 // mode=1: transaction failed - inform via LD
89 // mode=2: transaction failed - inform via ST
91 if (mode
== HtmCallbackMode_HTM_CMD
) {
92 SequencerRequest
* request
= nullptr;
94 assert(m_htmCmdRequestTable
.size() > 0);
96 request
= m_htmCmdRequestTable
.front();
97 m_htmCmdRequestTable
.pop_front();
99 assert(isHtmCmdRequest(request
->m_type
));
101 PacketPtr pkt
= request
->pkt
;
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
;
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
);
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());
138 DPRINTF(HtmMem
, "HTM_CMD: fail - htmUid=%u\n",
139 pkt
->getHtmTransactionUid());
142 rubyHtmCallback(pkt
, htm_return_code
);
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());
150 auto &seq_req_list
= m_RequestTable
[address
];
151 while (!seq_req_list
.empty()) {
152 SequencerRequest
&request
= seq_req_list
.front();
154 PacketPtr pkt
= request
.pkt
;
159 // store conditionals should indicate failure
160 if (request
.m_type
== RubyRequestType_Store_Conditional
) {
161 pkt
->req
->setExtraData(0);
164 DPRINTF(HtmMem
, "%s_FAIL: size=%d - "
165 "addr=0x%lx - htmUid=%d\n",
166 (mode
== HtmCallbackMode_LD_FAIL
) ? "LD" : "ST",
168 address
, pkt
->getHtmTransactionUid());
170 rubyHtmCallback(pkt
, htm_return_code
);
173 seq_req_list
.pop_front();
175 // free all outstanding requests corresponding to this address
176 if (seq_req_list
.empty()) {
177 m_RequestTable
.erase(address
);
180 panic("unrecognised HTM callback mode\n");
185 HTMSequencer::regStats()
187 Sequencer::regStats();
189 // hardware transactional memory
190 m_htm_transaction_cycles
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
)
196 m_htm_transaction_instructions
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
)
202 auto num_causes
= static_cast<int>(HtmFailureFaultCause::NUM_CAUSES
);
203 m_htm_transaction_abort_cause
205 .name(name() + ".htm_transaction_abort_cause")
206 .desc("cause of htm transaction abort")
207 .flags(Stats::total
| Stats::pdf
| Stats::dist
| Stats::nozero
)
210 for (unsigned cause_idx
= 0; cause_idx
< num_causes
; ++cause_idx
) {
211 m_htm_transaction_abort_cause
.subname(
213 htmFailureToStr(HtmFailureFaultCause(cause_idx
)));
218 HTMSequencer::rubyHtmCallback(PacketPtr pkt
,
219 const HtmFailedInCacheReason htm_return_code
)
221 // The packet was destined for memory and has not yet been turned
223 assert(system
->isMemAddr(pkt
->getAddr()) || system
->isDeviceMemAddr(pkt
));
224 assert(pkt
->isRequest());
226 // First retrieve the request port from the sender State
227 RubyPort::SenderState
*senderState
=
228 safe_cast
<RubyPort::SenderState
*>(pkt
->popSenderState());
230 MemSlavePort
*port
= safe_cast
<MemSlavePort
*>(senderState
->port
);
231 assert(port
!= nullptr);
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
);
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());
254 HTMSequencer::wakeup()
258 // Check for deadlock of any of the requests
259 Cycles current_time
= curCycle();
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();
267 for (; htm
!= htm_end
; ++htm
) {
268 SequencerRequest
* request
= *htm
;
269 if (current_time
- request
->issue_time
< m_deadlock_threshold
)
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()));
284 HTMSequencer::empty() const
286 return Sequencer::empty() && m_htmCmdRequestTable
.empty();
289 template <class VALUE
>
291 operator<<(ostream
&out
, const std::deque
<VALUE
> &queue
)
293 auto i
= queue
.begin();
294 auto end
= queue
.end();
297 for (; i
!= end
; ++i
)
305 HTMSequencer::print(ostream
& out
) const
307 Sequencer::print(out
);
309 out
<< "+ [HTMSequencer: " << m_version
310 << ", htm cmd request table: " << m_htmCmdRequestTable
314 // Insert the request in the request table. Return RequestStatus_Aliased
315 // if the entry was already present.
317 HTMSequencer::insertRequest(PacketPtr pkt
, RubyRequestType primary_type
,
318 RubyRequestType secondary_type
)
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
;
327 // insert request into HtmCmd queue
328 SequencerRequest
* htmReq
=
329 new SequencerRequest(pkt
, primary_type
, secondary_type
,
332 m_htmCmdRequestTable
.push_back(htmReq
);
333 return RequestStatus_Ready
;
335 return Sequencer::insertRequest(pkt
, primary_type
, secondary_type
);