/* This file is part of JOP, the Java Optimized Processor see <http://www.jopdesign.com/> Copyright (C) 2001-2008, Martin Schoeberl (martin@jopdesign.com) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ /** * RtThread.java */ package com.jopdesign.sys; import javax.safetycritical.Terminal; import com.jopdesign.io.IOFactory; import com.jopdesign.io.SysDevice; import joprt.RtThread; /** * @author Martin * * Implementation class for the real-time thread RtThread. * */ public class RtThreadImpl { /** * Helper class to start the other CPUs in a CMP system * @author martin * */ static class CMPStart implements Runnable { volatile boolean started; /* (non-Javadoc) * @see java.lang.Runnable#run() */ public void run() { // Disable all interrupts globally and local - for sure Native.wr(0, Const.IO_INT_ENA); Native.wr(0, Const.IO_INTMASK); // startThread for all threads that belong to this CPU. // This thread is already in state READY and not started. Scheduler s = Scheduler.sched[sys.cpuId]; for (int i=0; i<s.cnt; ++i) { s.ref[i].startThread(); } // add scheduler for the core s JVMHelp.addInterruptHandler(sys.cpuId, 0, s); started = true; // clear all pending interrupts (e.g. timer after reset) Native.wr(1, Const.IO_INTCLEARALL); // schedule timer in 10 ms Native.wr(RtThreadImpl.startTime, Const.IO_TIMER); Native.wr(1, Const.IO_INTCLEARALL); // enable all interrupts Native.wr(-1, Const.IO_INTMASK); Native.wr(1, Const.IO_INT_ENA); // nothing to do in the main thread for the CMP cores 1 .. n-1 for (;;) { ; } } } // usual priority levels of java.lang.Thread public final static int MIN_PRIORITY = 1; public final static int NORM_PRIORITY = 5; public final static int MAX_PRIORITY = 10; // priority levels above Thread protected final static int RT_BASE = 2; protected final static int RT_IDLE = 1; RtThread rtt; // reference to RtThread's run method private int priority; private int period; // period in us private int offset; // offset in us private boolean isEvent; // it's a software event // only used in startMission final static int CREATED = 0; final static int READY = 1; // READY means ready to run. final static int WAITING = 2; // active is the running thread. final static int DEAD = 3; int state; int cpuId; // core that the thread is running on // index in next, ref and event int nr; int[] stack; int sp; /** * The scope that the thread is in. * Set to initArea when started. */ Memory currentArea = null; /** * The scope for the the initial thread (and between missions). * Null until the Memory object that represents immortal is created. */ static Memory initArea; // linked list of threads in priority order // used only at initialization time to collect the threads private RtThreadImpl lower; private static RtThreadImpl head; static boolean initDone; static boolean mission; static SysDevice sys = IOFactory.getFactory().getSysDevice(); // no synchronization necessary: // doInit() is called on first new RtThread() => // only one (this calling) thread is now runnable. // // However, to avoid stack issues we can also call // explicit public static void init() { if (initDone==true) return; initDone = true; mission = false; head = null; // We have now more than one thread => // If we have 'normal' Threads we should start the timer! } RtThreadImpl(int prio, int us) { this(null, prio, us, 0); } public RtThreadImpl(RtThread rtt, int prio, int us, int off) { if (!initDone) { init(); } stack = new int[Const.STACK_SIZE-Const.STACK_OFF]; sp = Const.STACK_OFF; // default empty stack for GC before startMission() for (int i=0; i<Const.STACK_SIZE-Const.STACK_OFF; ++i) { stack[i] = 1234567; } this.rtt = rtt; period = us; offset = off; if (us==0) { // this is NOT a RT thread priority = prio; } else { // RT priority is above Thread priorities priority = prio+MAX_PRIORITY+RT_BASE; } state = CREATED; isEvent = false; // insert in linked list, priority ordered // highest priority first. // same priority is ordered as first created has // 'higher' priority. RtThreadImpl th = head; RtThreadImpl prev = null; while (th!=null && priority<=th.priority) { prev = th; th = th.lower; } lower = th; if (prev!=null) { prev.lower = this; } else { head = this; } } /** * Set the processor number * @param id */ public void setProcessor(int id) { cpuId = id; } private static void genInt() { // just schedule an interrupt // schedule() gets called. Native.wr(0, Const.IO_SWINT); for (int j=0;j<10;++j) ; } private void startThread() { if (state!=CREATED) return; // already called start // if we have interrupts enabled we have to synchronize if (period==0) { state = READY; // for the idle thread, but we're not using one } else { state = WAITING; } // set memory context to the current one currentArea = initArea; createStack(); // new thread starts right here after first scheduled if (mission) { // main (startMission) falls through rtt.run(); // if we arrive here it's time to delete runtime struct of thread // now do nothing! state = DEAD; for (;;) { // This will not work if we change stack like in Thread.java. // Then we have no reference to this. Scheduler.sched[sys.cpuId].next[nr] = Native.rd(Const.IO_US_CNT) + 2*Scheduler.IDL_TICK; genInt(); } } } /** * Create stack for the new thread. * Copy stack frame of main thread. * Could be reduced to copy only frames from * createStack() and startThread() and adjust the * frames to new position. */ private void createStack() { int i, j, k; i = Native.getSP(); // sp of createStack(); j = Native.rdIntMem(i-4); // sp of calling function j = Native.rdIntMem(j-4); // one more level of indirection sp = i-j+Const.STACK_OFF; k = j; for (; j<=i; ++j) { stack[j-k] = Native.rdIntMem(j); } // adjust stack frames k -= Const.STACK_OFF; // now difference between main stack and new stack stack[sp-Const.STACK_OFF-2] -= k; // saved vp stack[sp-Const.STACK_OFF-4] -= k; // saved sp j = stack[sp-Const.STACK_OFF-4]; stack[j-Const.STACK_OFF-2] -= k; stack[j-Const.STACK_OFF-4] -= k; /* this is the save version i = Native.getSP(); sp = i; for (j=Const.STACK_OFF; j<=i; ++j) { stack[j-Const.STACK_OFF] = Native.rdIntMem(j); } */ } // public void run() { // ; // nothing to do // } /** * Static start time of scheduling used by all cores */ static int startTime; public static void startMission() { int i, j, c; RtThreadImpl th; Scheduler s; if (!initDone) { init(); } // TODO: we also disable acquiring the global lock - a running GC // (on a different core) is not // protected for the write barriers at this time // Disable all interrupts globally and local - for sure Native.wr(0, Const.IO_INT_ENA); Native.wr(0, Const.IO_INTMASK); // if we have int's enabled for Thread scheduling // or using the Scheduler interrupt // we have to place a monitorenter here // Collect number of thread for each core th = head; for (c=0; th!=null; ++c) { Scheduler.sched[th.cpuId].cnt++; th = th.lower; } for (i=0; i<sys.nrCpu; ++i) { Scheduler.sched[i].allocArrays(); } // list is ordered with increasing priority // array is reverse priority ordered per core // top priority is last! for (th = head; th!=null; th = th.lower) { s = Scheduler.sched[th.cpuId]; s.ref[s.tmp] = th; th.nr = s.tmp; if (th.isEvent) { s.event[s.tmp] = Scheduler.EV_WAITING; } else { s.event[s.tmp] = Scheduler.NO_EVENT; } s.tmp--; } for (i=0; i<sys.nrCpu; ++i) { Scheduler.sched[i].addMain(); } // running threads (state!=CREATED) // are not started // TODO: where are 'normal' Threads placed? s = Scheduler.sched[sys.cpuId]; for (i=0; i<s.cnt; ++i) { s.ref[i].startThread(); } // wait 10 ms for the real start if the mission startTime = Native.rd(Const.IO_US_CNT)+10000; for (i=0; i<sys.nrCpu; ++i) { s = Scheduler.sched[i]; for (j=0; j<s.cnt; ++j) { s.next[j] = startTime+s.ref[j].offset; } } // add scheduler for the first core JVMHelp.addInterruptHandler(0, 0, Scheduler.sched[0]); CMPStart cmps[] = new CMPStart[sys.nrCpu-1]; // add the Runnables to start the other CPUs for (i=0; i<sys.nrCpu-1; ++i) { cmps[i] = new RtThreadImpl.CMPStart(); Startup.setRunnable(cmps[i], i); } // start the other CPUs sys.signal = 1; // busy wait for start threads of other cores for (;;) { boolean ready = true; for (i=0; i<sys.nrCpu-1; ++i) { ready = ready && cmps[i].started; } if (ready) { break; } } mission = true; // clear all pending interrupts (e.g. timer after reset) Native.wr(1, Const.IO_INTCLEARALL); // schedule timer in 10 ms Native.wr(startTime, Const.IO_TIMER); // enable all interrupts Native.wr(-1, Const.IO_INTMASK); Native.wr(1, Const.IO_INT_ENA); } public boolean waitForNextPeriod() { int nxt, now; Scheduler s = Scheduler.sched[sys.cpuId]; Native.wr(0, Const.IO_INT_ENA); nxt = s.next[nr] + period; now = Native.rd(Const.IO_US_CNT); if (nxt-now < 0) { // missed time! s.next[nr] = now; // correct next // next[nr] = nxt; // without correction! Native.wr(1, Const.IO_INT_ENA); return false; } else { s.next[nr] = nxt; } // state is not used in scheduling! // state = WAITING; // just schedule an interrupt // schedule() gets called. Native.wr(0, Const.IO_SWINT); // will arrive before return statement, // just after interrupt enable // TODO: do we really need this loop? for (int j=0;j<10;++j) ; Native.wr(1, Const.IO_INT_ENA); // This return should only be executed when we are // scheduled again return true; } public void setEvent() { isEvent = true; } public void fire() { Scheduler.sched[this.cpuId].event[this.nr] = Scheduler.EV_FIRED; // if prio higher... // should not be allowed befor startMission // TODO: for cross CPU event fire we need to generate the interrupt // for the other core! genInt(); } public void blockEvent() { Scheduler.sched[this.cpuId].event[this.nr] = Scheduler.EV_WAITING; // TODO: for cross CPU event fire we need to generate the interrupt // for the other core! genInt(); } /** * dummy yield() for compatibility reason. */ // public static void yield() {} /** * for 'soft' rt threads. */ public static void sleepMs(int millis) { int next = Native.rd(Const.IO_US_CNT)+millis*1000; while (Native.rd(Const.IO_US_CNT)-next < 0) { genInt(); } } final static int MIN_US = 10; /** * Waste CPU cycles to simulate work. * @param us execution time in us */ public static void busyWait(int us) { int t1, t2, t3; int cnt; cnt = 0; t1 = Native.rd(Const.IO_US_CNT); for (;;) { t2 = Native.rd(Const.IO_US_CNT); t3 = t2-t1; // System.out.println(cnt+" "+t3); t1 = t2; if (t3<MIN_US) { cnt += t3; } if (cnt>=us) { return; } } } // TODO: Decide how to protect the total root set (ref[].stack and active // stack while assembling it. Then some writebarrier should protect the // references and downgrade the GC state from black to grey? // TODO: make it CMP aware static int[] getStack(int num) { return Scheduler.sched[sys.cpuId].ref[num].stack; } static int getSP(int num) { return Scheduler.sched[sys.cpuId].ref[num].sp; } static int getCnt() { return Scheduler.sched[sys.cpuId].cnt; } static int getActive() { return Scheduler.sched[sys.cpuId].active; } public static RtThread currentRtThread() { Scheduler s = Scheduler.sched[sys.cpuId]; if (s.ref==null) { return null; } return s.ref[s.active].rtt; } static Memory getCurrentScope() { // JVMHelp.wr("getCurrent"); // we call it only when the mission is already started Scheduler s = Scheduler.sched[sys.cpuId]; return s.ref[s.active].currentArea; // but we could use that in general to encapsulate // scope set/get into this class // RtThreadImpl rtt = null; // if (Scheduler.sched==null) { // return null; // } // Scheduler s = Scheduler.sched[sys.cpuId]; // if (s!=null || s.ref!=null) { // int nr = s.active; // rtt = s.ref[nr]; // } // if (rtt==null) { // // we don't have started the mission // return null; // } else { // return rtt.currentArrea; // } } // static void setCurrentScope(Scope sc) { // RtThreadImpl rtt = null; // Scheduler s = Scheduler.sched[sys.cpuId]; // if (s!=null || s.ref!=null) { // int nr = s.active; // rtt = s.ref[nr]; // } // rtt.currentArrea = sc; // } static void trace(int[] stack, int sp) { int fp, mp, vp, addr, loc, args; int val; fp = sp-4; // first frame point is easy, since last sp points to the end of the frame while (fp>Const.STACK_OFF+5) { // stop befor 'fist' method mp = stack[fp+4-Const.STACK_OFF]; vp = stack[fp+2-Const.STACK_OFF]; val = Native.rdMem(mp); addr = val>>>10; // address of callee util.Dbg.intVal(addr); val = Native.rdMem(mp+1); // cp, locals, args args = val & 0x1f; loc = (val>>>5) & 0x1f; fp = vp+args+loc; // new fp can be calc. with vp and count of local vars } } }