/**
* Copyright (c) 2007, Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of MSPSim.
*
* -----------------------------------------------------------------
*
* MSP430
*
* Author : Joakim Eriksson
* Created : Sun Oct 21 22:00:00 2007
*/
package se.sics.mspsim.core;
import java.io.PrintStream;
import se.sics.mspsim.profiler.SimpleProfiler;
import se.sics.mspsim.util.ArrayUtils;
import se.sics.mspsim.util.ComponentRegistry;
import se.sics.mspsim.util.MapTable;
public class MSP430 extends MSP430Core {
private int[] execCounter;
private int[] trace;
private int tracePos;
private boolean debug = false;
private boolean running = false;
private boolean isBreaking = false;
private double rate = 2.0;
// Debug time - measure cycles
private long lastCycles = 0;
private long lastCpuCycles = 0;
private long time;
private long nextSleep = 0;
private long nextOut = 0;
private double lastCPUPercent = 0d;
private DisAsm disAsm;
private SimEventListener[] simEventListeners;
/**
* Creates a new <code>MSP430</code> instance.
*
*/
public MSP430(int type, ComponentRegistry registry, MSP430Config config) {
super(type, registry, config);
disAsm = new DisAsm();
}
public double getCPUPercent() {
return lastCPUPercent;
}
public DisAsm getDisAsm() {
return disAsm;
}
public void cpuloop() throws EmulationException {
if (isRunning()) {
throw new IllegalStateException("already running");
}
setRunning(true);
try {
// ??? - power-up should be executed?!
time = System.currentTimeMillis();
run();
} finally {
setRunning(false);
}
}
private void run() throws EmulationException {
while (!isStopping) {
if (cycles > nextOut && !debug) {
printCPUSpeed(reg[PC]);
nextOut = cycles + 20000007;
}
int pc = emulateOP(-1);
if (pc >= 0) {
if (execCounter != null) {
execCounter[pc]++;
}
if (trace != null) {
trace[tracePos++] = pc;
if (tracePos >= trace.length)
tracePos = 0;
}
// -------------------------------------------------------------------
// Debug information
// -------------------------------------------------------------------
if (debug) {
if (servicedInterrupt >= 0) {
disAsm.disassemble(pc, memory, reg, servicedInterrupt);
} else {
disAsm.disassemble(pc, memory, reg);
}
}
}
/* Just a test to see if it gets down to a reasonable speed */
if (cycles > nextSleep) {
try {
Thread.sleep(100);
} catch (Exception e) {
}
// Frequency = 100 * cycles ratio
// Ratio = Frq / 100
nextSleep = cycles + (long)(rate * dcoFrq / 10);
}
// if ((instruction & 0xff80) == CALL) {
// System.out.println("Call to PC = " + reg[PC]);
// }
}
isStopping = isBreaking = false;
}
/* Use stepInstructions or stepMicros instead */
@Deprecated public long step() throws EmulationException {
return stepMicros(1, 1);
}
public long stepInstructions(int count) throws EmulationException {
if (isRunning()) {
throw new IllegalStateException("step not possible when CPU is running");
}
setRunning(true);
try {
while (count > 0 && !isStopping) {
int pc = emulateOP(-1);
if (pc >= 0) {
count--;
if (execCounter != null) {
execCounter[pc]++;
}
if (trace != null) {
trace[tracePos++] = pc;
if (tracePos >= trace.length) {
tracePos = 0;
}
}
// -------------------------------------------------------------------
// Debug information
// -------------------------------------------------------------------
if (debug) {
if (servicedInterrupt >= 0) {
disAsm.disassemble(pc, memory, reg, servicedInterrupt);
} else {
disAsm.disassemble(pc, memory, reg);
}
}
}
}
} finally {
setRunning(false);
}
isStopping = isBreaking = false;
return cycles;
}
/* this represents the micros time that was "promised" last time */
/* NOTE: this is a delta compared to "current micros"
*/
long lastReturnedMicros;
long lastMicrosCycles;
boolean microClockReady = false;
/* when DCO has changed speed, this method will be called */
protected void dcoReset() {
microClockReady = false;
}
/*
* Perform a single step (even if in LPM) but no longer than to maxCycles + 1 instr
* Note: jumpMicros just jump the clock until that time
* executeMicros also check eventQ, etc and executes instructions
*/
long maxCycles = 0;
public long stepMicros(long jumpMicros, long executeMicros) throws EmulationException {
if (isRunning()) {
throw new IllegalStateException("step not possible when CPU is running");
}
if (jumpMicros < 0) {
throw new IllegalArgumentException("Can not jump a negative time: " + jumpMicros);
}
/* quick hack - if microdelta == 0 => ensure that we have correct zery cycles
*/
if (!microClockReady) {
lastMicrosCycles = maxCycles;
}
// Note: will be reset during DCO-syncs... => problems ???
lastMicrosDelta += jumpMicros;
if (microClockReady) {
/* check that we did not miss any events (by comparing with last return value) */
maxCycles = lastMicrosCycles + (lastMicrosDelta * dcoFrq) / 1000000;
if (cpuOff) {
if(maxCycles > nextEventCycles) {
/* back this time again... */
lastMicrosDelta -= jumpMicros;
printEventQueues(System.out);
throw new IllegalArgumentException("Jumping to a time that is further than possible in LPM maxCycles:" +
maxCycles + " cycles: " + cycles + " nextEventCycles: " + nextEventCycles);
}
} else if (maxCycles > cycles) {
/* back this time again... */
lastMicrosDelta -= jumpMicros;
throw new IllegalArgumentException("Jumping to a time that is further than possible not LPM maxCycles:" +
maxCycles + " cycles: " + cycles);
}
}
microClockReady = true;
/* run until this cycle time */
maxCycles = lastMicrosCycles + ((lastMicrosDelta + executeMicros) * dcoFrq) / 1000000;
/*System.out.println("Current cycles: " + cycles + " additional micros: " + (jumpMicros) +
" exec micros: " + executeMicros + " => Execute until cycles: " + maxCycles);*/
while (cycles < maxCycles || (cpuOff && (nextEventCycles < cycles))) {
int pc = emulateOP(maxCycles);
if (pc >= 0) {
if (execCounter != null) {
execCounter[pc]++;
}
if (trace != null) {
if (tracePos >= trace.length) {
tracePos = 0;
}
trace[tracePos++] = pc;
}
// -------------------------------------------------------------------
// Debug information
// -------------------------------------------------------------------
if (debug) {
if (servicedInterrupt >= 0) {
disAsm.disassemble(pc, memory, reg, servicedInterrupt);
} else {
disAsm.disassemble(pc, memory, reg);
}
}
}
if (isStopping) {
isStopping = false;
if (cycles < maxCycles || (cpuOff && (nextEventCycles < cycles))) {
// Did not complete the execution cycle
lastMicrosDelta -= jumpMicros;
}
if (isBreaking) {
isBreaking = false;
throw new BreakpointException();
}
lastReturnedMicros = 0;
return 0;
}
}
if (cpuOff && !(interruptsEnabled && servicedInterrupt == -1 && interruptMax >= 0)) {
lastReturnedMicros = (1000000 * (nextEventCycles - cycles)) / dcoFrq;
} else {
lastReturnedMicros = 0;
}
if(cycles < maxCycles) {
throw new RuntimeException("cycles < maxCycles : " + cycles + " < " + maxCycles);
}
if(lastReturnedMicros < 0) {
throw new RuntimeException("lastReturnedMicros < 0 : " + lastReturnedMicros);
}
return lastReturnedMicros;
}
public void stop() {
isStopping = true;
}
public void triggBreakpoint() {
isBreaking = true;
stop();
}
public int getDCOFrequency() {
return dcoFrq;
}
public int getExecCount(int address) {
if (execCounter != null) {
return execCounter[address];
}
return 0;
}
public void setMonitorExec(boolean mon) {
if (mon) {
if (execCounter == null) {
execCounter = new int[MAX_MEM];
}
} else {
execCounter = null;
}
}
public void setTrace(int size) {
if (size == 0) {
trace = null;
} else {
trace = new int[size];
}
tracePos = 0;
}
public int getBackTrace(int pos) {
int tPos = tracePos - pos - 1;
if (tPos < 0) {
tPos += trace.length;
}
return trace[tPos];
}
public int getTraceSize() {
return trace == null ? 0 : trace.length;
}
private void printCPUSpeed(int pc) {
// Passed time
int td = (int)(System.currentTimeMillis() - time);
// Passed total cycles
long cd = (cycles - lastCycles);
// Passed "active" CPU cycles
long cpud = (cpuCycles - lastCpuCycles);
if (td == 0 || cd == 0) {
return;
}
if (DEBUGGING_LEVEL > 0) {
System.out.println("Elapsed: " + td
+ " cycDiff: " + cd + " => " + 1000 * (cd / td )
+ " cyc/s cpuDiff:" + cpud + " => "
+ 1000 * (cpud / td ) + " cyc/s "
+ (10000 * cpud / cd)/100.0 + "%");
}
lastCPUPercent = (10000 * cpud / cd) / 100.0;
time = System.currentTimeMillis();
lastCycles = cycles;
lastCpuCycles = cpuCycles;
if (DEBUGGING_LEVEL > 0) {
disAsm.disassemble(pc, memory, reg);
}
}
public void generateTrace(PrintStream out) {
if (profiler != null && out != null) {
profiler.printStackTrace(out);
}
}
public boolean getDebug() {
return debug;
}
public void setDebug(boolean db) {
debug = db;
}
public void setMap(MapTable map) {
this.map = map;
/* When we got the map table we can also profile! */
if (profiler == null) {
setProfiler(new SimpleProfiler());
profiler.setCPU(this);
}
}
private void setRunning(boolean running) {
if (this.running != running) {
this.running = running;
if (running) {
isStopping = false;
isBreaking = false;
}
SimEventListener[] listeners = this.simEventListeners;
if (listeners != null) {
SimEvent.Type type = running ? SimEvent.Type.START : SimEvent.Type.STOP;
SimEvent event = new SimEvent(type);
for(SimEventListener l : listeners) {
l.simChanged(event);
}
}
}
}
public boolean isRunning() {
return running;
}
public double getExecutionRate() {
return rate;
}
public void setExecutionRate(double rate) {
this.rate = rate;
}
public synchronized void addSimEventListener(SimEventListener l) {
simEventListeners = ArrayUtils.add(SimEventListener.class, simEventListeners, l);
}
public synchronized void removeSimEventListener(SimEventListener l) {
simEventListeners = ArrayUtils.remove(simEventListeners, l);
}
}