1/*
2 * Copyright (c) 2007 MIPS Technologies, Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met: redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer;
9 * redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution;
12 * neither the name of the copyright holders nor the names of its
13 * contributors may be used to endorse or promote products derived from
14 * this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * Authors: Korey Sewell
29 */
30
31#ifndef __ARCH_MIPS_MT_HH__
32#define __ARCH_MIPS_MT_HH__
33
34/**
35 * @file
36 *
37 * ISA-specific helper functions for multithreaded execution.
38 */
39
40#include <iostream>
41
42#include "arch/mips/faults.hh"
43#include "arch/mips/isa_traits.hh"
44#include "arch/mips/mt_constants.hh"
45#include "arch/mips/pra_constants.hh"
46#include "arch/mips/registers.hh"
47#include "base/bitfield.hh"
48#include "base/logging.hh"
49#include "base/trace.hh"
50#include "cpu/exec_context.hh"
51
52namespace MipsISA
53{
54
55static inline RegVal
56readRegOtherThread(ThreadContext *tc, const RegId &reg,
57                   ThreadID tid=InvalidThreadID)
58{
59    ThreadContext *otc = nullptr;
60    if (tid != InvalidThreadID)
61        otc = tc->getCpuPtr()->getContext(tid);
62    else
63        otc = tc;
64
65    switch (reg.classValue()) {
66      case IntRegClass:
67        return otc->readIntReg(reg.index());
68        break;
69      case FloatRegClass:
70        return otc->readFloatReg(reg.index());
71        break;
72      case MiscRegClass:
73        return otc->readMiscReg(reg.index());
74      default:
75        panic("Unexpected reg class! (%s)", reg.className());
76    }
77}
78
79static inline void
80setRegOtherThread(ThreadContext *tc, const RegId& reg, RegVal val,
81                  ThreadID tid=InvalidThreadID)
82{
83    ThreadContext *otc = nullptr;
84    if (tid != InvalidThreadID)
85        otc = tc->getCpuPtr()->getContext(tid);
86    else
87        otc = tc;
88
89    switch (reg.classValue()) {
90      case IntRegClass:
91        return otc->setIntReg(reg.index(), val);
92        break;
93      case FloatRegClass:
94        return otc->setFloatReg(reg.index(), val);
95        break;
96      case MiscRegClass:
97        return otc->setMiscReg(reg.index(), val);
98      default:
99        panic("Unexpected reg class! (%s)", reg.className());
100    }
101}
102
103static inline RegVal
104readRegOtherThread(ExecContext *xc, const RegId &reg,
105                   ThreadID tid=InvalidThreadID)
106{
107    return readRegOtherThread(xc->tcBase(), reg, tid);
108}
109
110static inline void
111setRegOtherThread(ExecContext *xc, const RegId& reg, RegVal val,
112                  ThreadID tid=InvalidThreadID)
113{
114    setRegOtherThread(xc->tcBase(), reg, val, tid);
115}
116
117template <class TC>
118inline unsigned
119getVirtProcNum(TC *tc)
120{
121    TCBindReg tcbind = tc->readMiscRegNoEffect(MISCREG_TC_BIND);
122    return tcbind.curVPE;
123}
124
125template <class TC>
126inline unsigned
127getTargetThread(TC *tc)
128{
129    VPEControlReg vpeCtrl = tc->readMiscRegNoEffect(MISCREG_VPE_CONTROL);
130    return vpeCtrl.targTC;
131}
132
133template <class TC>
134inline void
135haltThread(TC *tc)
136{
137    if (tc->status() == TC::Active) {
138        tc->halt();
139
140        // Save last known PC in TCRestart
141        // @TODO: Needs to check if this is a branch and if so,
142        // take previous instruction
143        PCState pc = tc->pcState();
144        tc->setMiscReg(MISCREG_TC_RESTART, pc.npc());
145
146        warn("%i: Halting thread %i in %s @ PC %x, setting restart PC to %x",
147                curTick(), tc->threadId(), tc->getCpuPtr()->name(),
148                pc.pc(), pc.npc());
149    }
150}
151
152template <class TC>
153inline void
154restoreThread(TC *tc)
155{
156    if (tc->status() != TC::Active) {
157        // Restore PC from TCRestart
158        Addr restartPC = tc->readMiscRegNoEffect(MISCREG_TC_RESTART);
159
160        // TODO: SET PC WITH AN EVENT INSTEAD OF INSTANTANEOUSLY
161        tc->pcState(restartPC);
162        tc->activate();
163
164        warn("%i: Restoring thread %i in %s @ PC %x",
165                curTick(), tc->threadId(), tc->getCpuPtr()->name(), restartPC);
166    }
167}
168
169template <class TC>
170void
171forkThread(TC *tc, Fault &fault, int Rd_bits, int Rs, int Rt)
172{
173    MVPConf0Reg mvpConf = tc->readMiscRegNoEffect(MISCREG_MVP_CONF0);
174    int num_threads = mvpConf.ptc + 1;
175
176    int success = 0;
177    for (ThreadID tid = 0; tid < num_threads && success == 0; tid++) {
178        TCBindReg tidTCBind =
179            readRegOtherThread(tc, RegId(MiscRegClass, MISCREG_TC_BIND), tid);
180        TCBindReg tcBind = tc->readMiscRegNoEffect(MISCREG_TC_BIND);
181
182        if (tidTCBind.curVPE == tcBind.curVPE) {
183
184            TCStatusReg tidTCStatus =
185                readRegOtherThread(tc, RegId(MiscRegClass, MISCREG_TC_STATUS),
186                                       tid);
187
188            TCHaltReg tidTCHalt =
189                readRegOtherThread(tc, RegId(MiscRegClass, MISCREG_TC_HALT),
190                                       tid);
191
192            if (tidTCStatus.da == 1 && tidTCHalt.h == 0 &&
193                tidTCStatus.a == 0 && success == 0) {
194
195                setRegOtherThread(tc, RegId(MiscRegClass, MISCREG_TC_RESTART),
196                                      Rs, tid);
197                setRegOtherThread(tc, RegId(IntRegClass, Rd_bits), Rt, tid);
198
199                StatusReg status = tc->readMiscReg(MISCREG_STATUS);
200                TCStatusReg tcStatus = tc->readMiscReg(MISCREG_TC_STATUS);
201
202                // Set Run-State to Running
203                tidTCStatus.rnst = 0;
204                // Set Delay-Slot to 0
205                tidTCStatus.tds = 0;
206                // Set Dirty TC to 1
207                tidTCStatus.dt = 1;
208                // Set Activated to 1
209                tidTCStatus.a = 1;
210                // Set status to previous thread's status
211                tidTCStatus.tksu = status.ksu;
212                // Set ASID to previous thread's state
213                tidTCStatus.asid = tcStatus.asid;
214
215                // Write Status Register
216                setRegOtherThread(tc, RegId(MiscRegClass, MISCREG_TC_STATUS),
217                                      tidTCStatus, tid);
218
219                // Mark As Successful Fork
220                success = 1;
221            }
222        } else {
223            std::cerr << "Bad VPEs" << std::endl;
224        }
225    }
226
227    if (success == 0) {
228        VPEControlReg vpeControl =
229            tc->readMiscRegNoEffect(MISCREG_VPE_CONTROL);
230        vpeControl.excpt = 1;
231        tc->setMiscReg(MISCREG_VPE_CONTROL, vpeControl);
232        fault = std::make_shared<ThreadFault>();
233    }
234}
235
236
237template <class TC>
238int
239yieldThread(TC *tc, Fault &fault, int src_reg, uint32_t yield_mask)
240{
241    if (src_reg == 0) {
242        MVPConf0Reg mvpConf0 = tc->readMiscRegNoEffect(MISCREG_MVP_CONF0);
243        ThreadID num_threads = mvpConf0.ptc + 1;
244
245        int ok = 0;
246
247        // Get Current VPE & TC numbers from calling thread
248        TCBindReg tcBind = tc->readMiscRegNoEffect(MISCREG_TC_BIND);
249
250        for (ThreadID tid = 0; tid < num_threads; tid++) {
251            TCStatusReg tidTCStatus =
252                readRegOtherThread(tc, RegId(MiscRegClass, MISCREG_TC_STATUS),
253                                       tid);
254            TCHaltReg tidTCHalt =
255                readRegOtherThread(tc, RegId(MiscRegClass, MISCREG_TC_HALT),
256                                       tid);
257            TCBindReg tidTCBind =
258                readRegOtherThread(tc, RegId(MiscRegClass, MISCREG_TC_BIND),
259                                       tid);
260
261            if (tidTCBind.curVPE == tcBind.curVPE &&
262                tidTCBind.curTC == tcBind.curTC &&
263                tidTCStatus.da == 1 &&
264                tidTCHalt.h == 0    &&
265                tidTCStatus.a == 1) {
266                ok = 1;
267            }
268        }
269
270        if (ok == 1) {
271            TCStatusReg tcStatus = tc->readMiscRegNoEffect(MISCREG_TC_STATUS);
272            tcStatus.a = 0;
273            tc->setMiscReg(MISCREG_TC_STATUS, tcStatus);
274            warn("%i: Deactivating Hardware Thread Context #%i",
275                    curTick(), tc->threadId());
276        }
277    } else if (src_reg > 0) {
278        if (src_reg && !yield_mask != 0) {
279            VPEControlReg vpeControl = tc->readMiscReg(MISCREG_VPE_CONTROL);
280            vpeControl.excpt = 2;
281            tc->setMiscReg(MISCREG_VPE_CONTROL, vpeControl);
282            fault = std::make_shared<ThreadFault>();
283        } else {
284        }
285    } else if (src_reg != -2) {
286        TCStatusReg tcStatus = tc->readMiscRegNoEffect(MISCREG_TC_STATUS);
287        VPEControlReg vpeControl =
288            tc->readMiscRegNoEffect(MISCREG_VPE_CONTROL);
289
290        if (vpeControl.ysi == 1 && tcStatus.dt == 1 ) {
291            vpeControl.excpt = 4;
292            fault = std::make_shared<ThreadFault>();
293        } else {
294        }
295    }
296
297    return src_reg & yield_mask;
298}
299
300
301// TC will usually be a object derived from ThreadContext
302// (src/cpu/thread_context.hh)
303template <class TC>
304inline void
305updateStatusView(TC *tc)
306{
307    // TCStatus' register view must be the same as
308    // Status register view for CU, MX, KSU bits
309    TCStatusReg tcStatus = tc->readMiscRegNoEffect(MISCREG_TC_STATUS);
310    StatusReg status = tc->readMiscRegNoEffect(MISCREG_STATUS);
311
312    status.cu = tcStatus.tcu;
313    status.mx = tcStatus.tmx;
314    status.ksu = tcStatus.tksu;
315
316    tc->setMiscRegNoEffect(MISCREG_STATUS, status);
317}
318
319// TC will usually be a object derived from ThreadContext
320// (src/cpu/thread_context.hh)
321template <class TC>
322inline void
323updateTCStatusView(TC *tc)
324{
325    // TCStatus' register view must be the same as
326    // Status register view for CU, MX, KSU bits
327    TCStatusReg tcStatus = tc->readMiscRegNoEffect(MISCREG_TC_STATUS);
328    StatusReg status = tc->readMiscRegNoEffect(MISCREG_STATUS);
329
330    tcStatus.tcu = status.cu;
331    tcStatus.tmx = status.mx;
332    tcStatus.tksu = status.ksu;
333
334    tc->setMiscRegNoEffect(MISCREG_TC_STATUS, tcStatus);
335}
336
337} // namespace MipsISA
338
339
340#endif
341