ADC12Plus, AES128, CRC16 and RTC peripheral implementation

This commit is contained in:
lebrush 2013-05-16 17:17:37 +02:00
parent 968187d8f3
commit ab7e6b00b4
4 changed files with 1749 additions and 0 deletions

View File

@ -0,0 +1,415 @@
/**
* 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.
*
* $Id$
*
* -----------------------------------------------------------------
*
* ADC12
*
* Each time a sample is converted the ADC12 system will check for EOS flag
* and if not set it just continues with the next conversion (x + 1).
* If EOS next conversion is startMem.
* Interrupt is triggered when the IE flag are set!
*
*
* Author : Joakim Eriksson
* Created : Sun Oct 21 22:00:00 2007
* Updated : $Date$
* $Revision$
*/
package com.tado.mspsim.core;
import java.util.Arrays;
import se.sics.mspsim.core.ADCInput;
import se.sics.mspsim.core.IOUnit;
import se.sics.mspsim.core.MSP430Core;
import se.sics.mspsim.core.TimeEvent;
import se.sics.mspsim.core.EmulationLogger.WarningType;
/**
* ADC12 Plus IO peripheral
*
* This module extends the functionality of the ADC12 by adding one
* configuration register which controls the power consumption of this
* peripheral. Additionally, implements several resolutions (8 to 12-bit)
*
* The rest of functionalities are the same as the standard ADC12
*
* @author Víctor Ariño <victor.arino@tado.com>
*/
public class ADC12Plus extends IOUnit {
/**
* Address and size for IO configuration
*/
public static int OFFSET = 0x0700;
public static int SIZE = 0x3e;
public static final int ADC12CTL0 = 0x00;// Reset with POR
public static final int ADC12CTL1 = 0x02;// Reset with POR
public static final int ADC12CTL2 = 0x04;// Reset with POR XXX
public static final int ADC12IFG = 0x0a; // Reset with POR
public static final int ADC12IE = 0x0c; // Reset with POR
public static final int ADC12IV = 0x0e; // Reset with POR
public static final int ADC12MEM0 = 0x20; // Unchanged
public static final int ADC12MEM1 = 0x22; // Unchanged
public static final int ADC12MEM2 = 0x24; // Unchanged
public static final int ADC12MEM3 = 0x26; // Unchanged
public static final int ADC12MEM4 = 0x28; // Unchanged
public static final int ADC12MEM5 = 0x2A; // Unchanged
public static final int ADC12MEM6 = 0x2C; // Unchanged
public static final int ADC12MEM7 = 0x2E; // Unchanged
public static final int ADC12MEM8 = 0x30; // Unchanged
public static final int ADC12MEM9 = 0x32; // Unchanged
public static final int ADC12MEM10 = 0x34; // Unchanged
public static final int ADC12MEM11 = 0x36; // Unchanged
public static final int ADC12MEM12 = 0x38; // Unchanged
public static final int ADC12MEM13 = 0x3A; // Unchanged
public static final int ADC12MEM14 = 0x3C; // Unchanged
public static final int ADC12MEM15 = 0x3E; // Unchanged
public static final int ADC12MCTL0 = 0x0010; // Reset with POR
public static final int ADC12MCTL1 = 0x0011; // Reset with POR
public static final int ADC12MCTL2 = 0x0012; // Reset with POR
public static final int ADC12MCTL3 = 0x0013; // Reset with POR
public static final int ADC12MCTL4 = 0x0014; // Reset with POR
public static final int ADC12MCTL5 = 0x0015; // Reset with POR
public static final int ADC12MCTL6 = 0x0016; // Reset with POR
public static final int ADC12MCTL7 = 0x0017; // Reset with POR
public static final int ADC12MCTL8 = 0x0018; // Reset with POR
public static final int ADC12MCTL9 = 0x0019; // Reset with POR
public static final int ADC12MCTL10 = 0x001A; // Reset with POR
public static final int ADC12MCTL11 = 0x001B; // Reset with POR
public static final int ADC12MCTL12 = 0x001C; // Reset with POR
public static final int ADC12MCTL13 = 0x001D; // Reset with POR
public static final int ADC12MCTL14 = 0x001E; // Reset with POR
public static final int ADC12MCTL15 = 0x001F; // Reset with POR
public static final int[] SHTBITS = new int[] { 4, 8, 16, 32, 64, 96, 128,
192, 256, 384, 512, 768, 1024, 1024, 1024, 1024 };
public static final int BUSY_MASK = 0x01;
public static final int EOS_MASK = 0x80;
public static final int CONSEQ_SINGLE = 0x00;
public static final int CONSEQ_SEQUENCE = 0x01;
public static final int CONSEQ_REPEAT_SINGLE = 0x02;
public static final int CONSEQ_REPEAT_SEQUENCE = 0x03;
public static final int CONSEQ_SEQUENCE_MASK = 0x01;
private int adc12ctl0 = 0;
private int adc12ctl1 = 0;
private int adc12ctl2 = 0;
private int[] adc12mctl = new int[16];
private int[] adc12mem = new int[16];
private int adc12Pos = 0;
private int shTime0 = 4;
private int shTime1 = 4;
private boolean adc12On = false;
private boolean enableConversion;
private boolean startConversion;
private boolean isConverting;
private int shSource = 0;
private int startMem = 0;
private int adcDiv = 1;
private ADCInput adcInput[] = new ADCInput[16];
private int conSeq;
private int adc12ie;
private int adc12ifg;
private int adc12iv;
private int adcSSel;
private int adc12Vector = 0x38;
private TimeEvent adcTrigger = new TimeEvent(0) {
public void execute(long t) {
// System.out.println(getName() + " **** executing update timers at " +
// t + " cycles=" + cpu.cycles);
convert();
}
};
/* These are CTL2 variables */
private int bitsResolution = 12;
private boolean formatSigned = false;
private int clockPredivider = 1;
/* Reference voltage 2.5V or 1.5V */
private boolean ref25V = false;
public ADC12Plus(MSP430Core cpu, int offset, int intVector) {
super("ADC12Plus", cpu, cpu.memory, offset);
adc12Vector = intVector;
}
public void reset(int type) {
enableConversion = false;
startConversion = false;
isConverting = false;
adc12ctl0 = 0;
adc12ctl1 = 0;
adc12ctl2 = 0;
shTime0 = shTime1 = 4;
adc12On = false;
shSource = 0;
startMem = adc12Pos = 0;
adcDiv = 1;
conSeq = 0;
adc12ie = 0;
adc12ifg = 0;
adc12iv = 0;
adcSSel = 0;
Arrays.fill(adc12mctl, 0);
clockPredivider = 1;
formatSigned = false;
bitsResolution = 12;
ref25V = false;
}
public void setADCInput(int adindex, ADCInput input) {
adcInput[adindex] = input;
}
/**
* Get the maximum input voltage set by the configuration of the registers
* (in mV)
*
* @return
*/
public int getMaxInVoltage() {
if (ref25V) {
return 2500;
} else {
return 1500;
}
}
/**
* Get the minimum in voltage set by the registers (in mV)
*
* @return
*/
public int getMinInVoltage() {
// TODO
return 0;
}
// write a value to the IO unit
public void write(int address, int value, boolean word, long cycles) {
address -= offset;
switch (address) {
case ADC12CTL0:
if (enableConversion) {
// Ongoing conversion: only some parts may be changed
adc12ctl0 = (adc12ctl0 & 0xfff0) + (value & 0xf);
} else {
adc12ctl0 = value;
shTime0 = SHTBITS[(value >> 8) & 0x0f];
shTime1 = SHTBITS[(value >> 12) & 0x0f];
adc12On = (value & 0x10) > 0;
}
enableConversion = (value & 0x02) > 0;
startConversion = (value & 0x01) > 0;
ref25V = (value & 0x20) > 0;
if (DEBUG)
log("Set SHTime0: " + shTime0 + " SHTime1: " + shTime1 + " ENC:"
+ enableConversion + " Start: " + startConversion
+ " ADC12ON: " + adc12On);
if (adc12On && enableConversion && startConversion && !isConverting) {
// Set the start time to be now!
isConverting = true;
adc12Pos = startMem;
int delay = clockPredivider * adcDiv
* ((adc12Pos < 8 ? shTime0 : shTime1) + 13);
cpu.scheduleTimeEvent(adcTrigger, cpu.getTime() + delay);
}
break;
case ADC12CTL1:
if (enableConversion) {
// Ongoing conversion: only some parts may be changed
adc12ctl1 = (adc12ctl1 & 0xfff8) + (value & 0x6);
} else {
adc12ctl1 = value & 0xfffe;
startMem = (value >> 12) & 0xf;
shSource = (value >> 10) & 0x3;
adcDiv = ((value >> 5) & 0x7) + 1;
adcSSel = (value >> 3) & 0x03;
}
conSeq = (value >> 1) & 0x03;
if (DEBUG)
log("Set startMem: " + startMem + " SHSource: " + shSource
+ " ConSeq-mode:" + conSeq + " Div: " + adcDiv + " ADCSSEL: "
+ adcSSel);
break;
case ADC12CTL2: /* Low Power Specs */
if (enableConversion) {
/*
* Clock pre divider can't be modified when conversion is already
* enabled
*/
value &= 0xfeff;
value |= (adc12ctl2 & 0x100);
}
clockPredivider = ((value & 0x100) > 0) ? 4 : 1;
/* bit resolution 8, 10, 12 */
int tmp = (value & 0x30) >> 4;
tmp = (tmp <= 2) ? tmp * 2 : 4;
bitsResolution = tmp + 8;
formatSigned = ((value & 0x08) > 0);
if (formatSigned) {
logw(WarningType.EMULATION_ERROR, "signed format not implemented");
}
adc12ctl2 = value;
break;
case ADC12IE:
adc12ie = value;
break;
case ADC12IFG:
adc12ifg = value;
break;
default:
if (address >= ADC12MCTL0 && address <= ADC12MCTL15) {
if (enableConversion) {
/* Ongoing conversion: not possible to modify */
} else {
adc12mctl[address - ADC12MCTL0] = value & 0xff;
if (DEBUG)
log("ADC12MCTL" + (address - ADC12MCTL0) + " source = "
+ (value & 0xf)
+ (((value & EOS_MASK) != 0) ? " EOS bit set" : ""));
}
}
}
}
// read a value from the IO unit
public int read(int address, boolean word, long cycles) {
address -= offset;
switch (address) {
case ADC12CTL0:
return adc12ctl0;
case ADC12CTL1:
return isConverting ? (adc12ctl1 | BUSY_MASK) : adc12ctl1;
case ADC12IE:
return adc12ie;
case ADC12IFG:
return adc12ifg;
default:
if (address >= ADC12MCTL0 && address <= ADC12MCTL15) {
return adc12mctl[address - ADC12MCTL0];
} else if (address >= ADC12MEM0 && address <= ADC12MEM15) {
int reg = (address - ADC12MEM0) / 2;
// Clear ifg!
adc12ifg &= ~(1 << reg);
// System.out.println("Read ADCMEM" + (reg / 2));
if (adc12iv == reg * 2 + 6) {
cpu.flagInterrupt(adc12Vector, this, false);
adc12iv = 0;
// System.out.println("** de-Trigger ADC12 IRQ for ADCMEM" +
// adc12Pos);
}
return adc12mem[reg];
}
}
return 0;
}
int smp = 0;
private void convert() {
// If off then just return...
if (!adc12On) {
isConverting = false;
return;
}
boolean runAgain = enableConversion && conSeq != CONSEQ_SINGLE;
// Some noise...
ADCInput input = adcInput[adc12mctl[adc12Pos] & 0xf];
int reading = input != null ? input.nextData() : 2048 + 100 - smp & 255;
/* Adapt resolution to 8, 10, 12 bits */
float percent = (reading - getMinInVoltage()) * 1f
/ ((getMaxInVoltage() - getMinInVoltage()) * 1f);
reading = (int) (((1L << bitsResolution) - 1) * percent);
adc12mem[adc12Pos] = reading;
smp += 7;
adc12ifg |= (1 << adc12Pos);
if ((adc12ie & (1 << adc12Pos)) > 0) {
// This should check if there already is an higher iv!
adc12iv = adc12Pos * 2 + 6;
// System.out.println("** Trigger ADC12 IRQ for ADCMEM" + adc12Pos);
cpu.flagInterrupt(adc12Vector, this, true);
}
if ((conSeq & CONSEQ_SEQUENCE_MASK) != 0) {
// Increase
if ((adc12mctl[adc12Pos] & EOS_MASK) == EOS_MASK) {
adc12Pos = startMem;
if (conSeq == CONSEQ_SEQUENCE) {
// Single sequence only
runAgain = false;
}
} else {
adc12Pos = (adc12Pos + 1) & 0x0f;
}
}
if (!runAgain) {
isConverting = false;
} else {
int delay = clockPredivider * adcDiv
* ((adc12Pos < 8 ? shTime0 : shTime1) + 13);
cpu.scheduleTimeEvent(adcTrigger, adcTrigger.getTime() + delay);
}
}
public void interruptServiced(int vector) {
}
/**
* Get the reference voltage if it is 1.5 volts or 2.5V
*
* @return
*/
public boolean isRef25V() {
return ref25V;
}
}

View File

@ -0,0 +1,423 @@
package com.tado.mspsim.core;
import java.nio.ByteBuffer;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import se.sics.mspsim.core.IOUnit;
import se.sics.mspsim.core.MSP430Core;
import se.sics.mspsim.core.EmulationLogger.WarningType;
/**
* AES128 msp430 peripheral emulation
*
* TODO:
* advanced cipher modes
* longer key support
* interrupts
* timing concerns
* first round key operations
*
* @author Víctor Ariño <victor.arino@tado.com>
*/
public class AES128 extends IOUnit {
/**
* Address and size for IO configuration
*/
public static int OFFSET = 0x09C0;
public static int SIZE = 12;
/**
* Enable/Disable debug output of the module
*/
private static boolean DEBUG = false;
/**
* Register definition as in the documentation Offset from 0xff80
*/
public static final int AES_VECTOR = 0x005A;
/* Main registers */
public static final int AESACTL0 = 0x00;
public static final int AESACTL1 = 0x02;
public static final int AESASTAT = 0x04;
public static final int AESAKEY = 0x06;
public static final int AESADIN = 0x08;
public static final int AESADOUT = 0x0a;
/* AESACTL0 Control Bits */
public static final int AESSWRST = 0x0080;
public static final int AESRDYIFG = 0x0100;
public static final int AESERRFG = 0x0800;
public static final int AESRDYIE = 0x1000;
public static final int AESOP_0 = 0x0000;
public static final int AESOP_1 = 0x0001;
public static final int AESOP_2 = 0x0002;
public static final int AESOP_3 = 0x0003;
/* AESASTAT Control Bits */
public static final int AESBUSY = 0x0001;
public static final int AESKEYWR = 0x0002;
public static final int AESDINWR = 0x0004;
public static final int AESDOUTRD = 0x0008;
public static final int AESCMEN = 0x8000;
/* Cipher modes */
private static final int MODE_ECB = 0;
private static final int MODE_CBC = 1;
private static final int MODE_OFB = 2;
private static final int MODE_CFB = 3;
/* Key lengths */
private static final int KEY_128 = 0;
private static final int KEY_192 = 1;
private static final int KEY_256 = 2;
/**
* Emulate peripheral using the default offset
*
* @param cpu
*/
public AES128(MSP430Core cpu) {
this(cpu, OFFSET);
}
/**
* Emulate peripheral using custom offset
*
* @param cpu
* @param offset
*/
public AES128(MSP430Core cpu, int offset) {
super("CRC", cpu, cpu.memory, offset);
}
/**
* Clear everything when reset. The cleared fields are specified in the
* manual
*/
public void reset(int type) {
/*
* AES software reset. Immediately resets the complete AES accelerator
* module even when busy except for the AESRDYIE, the AESKLx and the
* AESOPx bits. It also clears the (internal) state memory.
*/
key.clear();
inData.clear();
outData.clear();
isBusy = false;
advancedCipherMode = false;
interruptEnable = false;
errorFlag = false;
resetFlag = false;
cipherMode = MODE_ECB;
cipherBlockCounter = 0;
}
/**
* Variable holders for the different registers needed by this peripheral
*/
private ByteBuffer key = ByteBuffer.allocate(16);
private ByteBuffer inData = ByteBuffer.allocate(16);
private ByteBuffer outData = ByteBuffer.allocate(16);
/**
* Syntax sugar
*/
private boolean isBusy = false;
/**
* Nice names for buffer operations :-)
*/
private int bytesReadOut() {
return outData.position();
}
private int bytesWrittenIn() {
return inData.position();
}
private int bytesKeyWritten() {
return key.position();
}
private boolean allBytesReadOut() {
return !outData.hasRemaining();
}
private boolean isKeyReady() {
return !key.hasRemaining();
}
private boolean isDataReady() {
return !inData.hasRemaining();
}
private int getStatReg() {
int stat = 0;
stat |= (bytesReadOut() & 0x0f) << 12;
stat |= (bytesWrittenIn() & 0x0f) << 8;
stat |= (bytesKeyWritten() & 0x0f) << 4;
stat |= (allBytesReadOut() ? 1 : 0) << 3;
stat |= ((isDataReady() ? 1 : 0) & 0x01) << 2;
stat |= ((isKeyReady() ? 1 : 0) & 0x01) << 1;
stat |= ((isBusy ? 1 : 0) & 0x01);
return stat & 0xffff;
}
/* AESACTL0 register */
private boolean advancedCipherMode = false;
private boolean interruptEnable = false;
private boolean errorFlag = false;
private boolean readyInterruptFlag = false;
private boolean resetFlag = false;
private int cipherMode = MODE_ECB;
private int keyLength = KEY_128;
private int operation = AESOP_0;
private int cipherBlockCounter = 0;
/**
* CTL0 Register built upon variables
*
* @return uint16_t register
*/
private int getCTL0Reg() {
int ctl0 = 0;
ctl0 |= ((advancedCipherMode ? 1 : 0) & 0x01) << 15;
ctl0 |= ((interruptEnable ? 1 : 0) & 0x01) << 12;
ctl0 |= ((errorFlag ? 1 : 0) & 0x01) << 11;
ctl0 |= ((readyInterruptFlag ? 1 : 0) & 0x01) << 8;
ctl0 |= ((resetFlag ? 1 : 0) & 0x01) << 7;
ctl0 |= (cipherMode & 0x03) << 5;
ctl0 |= (keyLength & 0x03) << 2;
ctl0 |= (operation & 0x03);
return ctl0 & 0xffff;
}
private int getCTL1Reg() {
int ctl1 = cipherBlockCounter;
return ctl1 & 0x000f;
}
/**
* Java implementation of the AES encryption algorithm
*
* This method encrypts whatever is in inData using key and sets it into
* outData
*/
private void aesEncrypt() {
log("encrypt");
Cipher cipher;
SecretKeySpec spec = new SecretKeySpec(key.array(), "AES");
try {
cipher = Cipher.getInstance("AES/ECB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, spec);
byte[] bytes = cipher.doFinal(inData.array());
outData.clear();
outData.put(bytes);
outData.rewind();
} catch (Exception e) {
log(e.getStackTrace().toString());
}
}
/**
* Java implementation of the AES decryption algorithm
*
* This method decrypts whatever is in inData using key and sets it into
* outData
*/
private void aesDecrypt() {
log("decrypt");
Cipher cipher;
SecretKeySpec spec = new SecretKeySpec(key.array(), "AES");
try {
cipher = Cipher.getInstance("AES/ECB/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, spec);
byte[] bytes = cipher.doFinal(inData.array());
outData.clear();
outData.put(bytes);
outData.rewind();
} catch (Exception e) {
log(e.getStackTrace().toString());
}
}
/*
* The inherited log function is not working for whatever reason. A quick
* redefinition helps a lot while developing the module
*/
@Override
protected void log(String msg) {
if (DEBUG) {
System.out.println(msg);
}
}
/**
* Log using printf format
*
* @param format
* @param arguments
*/
protected void log(final String format, final Object... arguments) {
if (DEBUG) {
System.out.printf(format, arguments);
}
}
/**
* The registers are written
*/
public void write(int address, int value, boolean word, long cycles) {
log("write @ %x <-- %x (word=%b)\n", address, value, word);
int lo = (value) & 0xff; // low byte
int hi = (value >> 8) & 0xff; // high byte
switch (address - offset) {
case AESACTL0:
if ((value & AESSWRST) == AESSWRST) {
reset(0);
}
if (!(advancedCipherMode && cipherBlockCounter > 0)) {
advancedCipherMode = ((value & AESCMEN) == AESCMEN);
}
interruptEnable = ((value & AESRDYIE) == AESRDYIE);
errorFlag = ((value & AESERRFG) == AESERRFG);
readyInterruptFlag = ((value & AESRDYIFG) == AESRDYIFG);
if (!(advancedCipherMode && cipherBlockCounter > 0)
&& !advancedCipherMode) {
cipherMode = ((value & 0x60) >> 5);
}
keyLength = ((value & 0xC) >> 2);
if (!(advancedCipherMode && cipherBlockCounter > 0)) {
operation = value & 0x03;
}
break;
case AESACTL1:
/* Only lower byte allowed */
if (!(advancedCipherMode && cipherBlockCounter > 0)) {
value &= 0x000f;
cipherBlockCounter = value;
}
break;
case AESASTAT:
/* Only two fields can be written in this register */
value &= (AESDINWR | AESKEYWR);
if ((value & AESKEYWR) == 0) {
/*
* This flag can only be cleared if the advanceModeSupport is not
* enabled (ref. User Guide)
*/
if (!advancedCipherMode) {
value |= AESKEYWR;
} else {
key.rewind();
}
} else {
isBusy = true;
key.position(key.limit());
switch (operation) {
case AESOP_0: // encrypt
aesEncrypt();
break;
case AESOP_1: // decrypt
aesDecrypt();
break;
case AESOP_2: // gen 1st round key
logw(WarningType.ILLEGAL_IO_WRITE, "to implement");
break;
case AESOP_3: // decrypt 1st round key
logw(WarningType.ILLEGAL_IO_WRITE, "to implement");
break;
}
isBusy = false;
readyInterruptFlag = true;
}
if ((value & AESDINWR) == 0) {
if (!advancedCipherMode) {
value |= AESKEYWR;
} else {
inData.rewind();
}
} else {
inData.position(inData.limit());
}
break;
case AESAKEY:
if (key.hasRemaining()) {
/* Clear the ready interrupt flag */
readyInterruptFlag = false;
key.put((byte) lo);
if (word && key.hasRemaining()) {
key.put((byte) hi);
}
}
break;
case AESADIN:
if (inData.hasRemaining()) {
/* Clear the ready interrupt flag */
readyInterruptFlag = false;
inData.put((byte) lo);
if (word && inData.hasRemaining()) {
inData.put((byte) hi);
}
}
break;
}
log("ctl0: %04x\nstat: %04x\n", getCTL0Reg(), getStatReg());
}
/**
* Registers are read
*/
public int read(int address, boolean word, long cycles) {
log("read %x (word?%b)\n", address, word);
switch (address - offset) {
case AESACTL0:
return getCTL0Reg();
case AESACTL1:
if (advancedCipherMode) {
return cipherBlockCounter;
}
case AESASTAT:
return getStatReg();
case AESADOUT:
if (isDataReady()) {
/* Clear the ready interrupt flag */
readyInterruptFlag = false;
if (outData.hasRemaining()) {
int temp = outData.get() & 0xff;
if (word && outData.hasRemaining()) {
temp |= (outData.get() << 8) & 0xff00;
}
return temp & 0xffff;
}
}
}
return 0;
}
public void interruptServiced(int vector) {
if (vector == AES_VECTOR) {
readyInterruptFlag = false;
}
}
}

View File

@ -0,0 +1,221 @@
package com.tado.mspsim.core;
import se.sics.mspsim.core.IOUnit;
import se.sics.mspsim.core.Loggable;
import se.sics.mspsim.core.MSP430Core;
/**
* CRC16 module for the MSP430
*
* @author Victor Ariño <victor.arino@tado.com>
*/
public class CRC16 extends IOUnit {
/**
* Address and size for IO configuration
*/
public static final int OFFSET = 0x0150;
public static final int SIZE = 8;
/**
* Register offsets definition as in the documentation.
*/
public static final int CRCDI = 0;
public static final int CRCDIRB = 0x2;
public static final int CRCINIRES = 0x4;
public static final int CRCRESR = 0x6;
/**
* Initial seed as recommended by CCITT
*/
private static final int CCITTSeed = 0xFFFF;
/**
* CRC16 peripheral for the MSP430 using the default memory offset
*
* @param cpu
*/
public CRC16(MSP430Core cpu) {
this(cpu, OFFSET);
}
/**
* CRC16 peripheral for the MSP430
*
* @param cpu
* CPU core
* @param offset
* Address offset, by default is 0x0150
*/
public CRC16(MSP430Core cpu, int offset) {
super("CRC16", cpu, cpu.memory, offset);
setLogLevel(Loggable.DEBUG);
}
/**
* CRC16 CCITT Java implementation
*
* @author Víctor Ariño <victor.arino@tado.com>
*/
private class CRC16Java {
private int crc = CCITTSeed;
private int polynomial = 0x1021; // 0001 0000 0010 0001 (0, 5, 12)
/**
* Adds one byte to the CRC computation
*
* @param b
* byte to add
*/
protected void process(byte b) {
for (int i = 0; i < 8; i++) {
boolean bit = ((b >> (7 - i) & 1) == 1);
boolean c15 = ((crc >> 15 & 1) == 1);
crc <<= 1;
if (c15 ^ bit)
crc ^= polynomial;
}
}
/**
* Add one byte in reverse way
*
* @param b
* byte to add
*/
protected void processRb(byte b) {
process(reflectByte(b));
}
/**
* Set the seed of the CRC
*
* @param seed
*/
protected void reset(int seed) {
crc = seed;
}
/**
* Get computed CRC
*
* @return
*/
protected int getCRC() {
return crc;
}
/**
* Reflect a byte
*
* @author Valentin Sawadski <valentin@tado.com>
* @param b
* byte to reflect
* @return reversed byte
*/
private byte reflectByte(byte b) {
byte ret = 0;
for (int i = 0; i < 8; i++) {
if ((b & (1 << i)) == (1 << i)) {
ret += (1 << (7 - i));
}
}
return ret;
}
/**
* Reflect all bytes of the CRC
*
* @author Valentin Sawadski <valentin@tado.com>
* @param crc
* @return crc reversed
*/
private int reflectCrcBytewise(int crc) {
int lowByte = reflectByte((byte) (crc & 0xFF)) & 0xFF;
int highByte = reflectByte((byte) ((crc & 0xFF00) >> 8)) & 0xFF;
return ((highByte << 8) + lowByte) & 0xFFFF;
}
/**
* Swap two bytes
*
* @author Valentin Sawadski <valentin@tado.com>
* @param crc
* @return swapped bytes
*/
private int swapBytes(int crc) {
int lowByte = crc & 0xFF;
int highByte = crc & 0xFF00;
return ((lowByte << 8) + (highByte >> 8)) & 0xFFFF;
}
/**
* Get the CRC reversed
*
* @return reversed crc
*/
protected int getCrcRb() {
return swapBytes(reflectCrcBytewise(crc));
}
}
private CRC16Java crc = new CRC16Java();
/**
* Clear everything when reset
*/
public void reset(int type) {
crc.reset(CCITTSeed);
}
/**
* The registers are written
*/
public void write(int address, int value, boolean word, long cycles) {
/*
* The offset variable is used in case the peripheral changes the base
* address in some microcontroller model
*/
switch (address - offset) {
case CRC16.CRCDI:
if (word) {
int hi = (value >> 8) & 0x00ff;
int lo = (value) & 0x00ff;
crc.processRb((byte) lo);
crc.processRb((byte) hi);
} else {
crc.processRb((byte) value);
}
break;
case CRC16.CRCDIRB:
if (word) {
int hi = (value >> 8) & 0x00ff;
int lo = (value) & 0x00ff;
crc.process((byte) hi);
crc.process((byte) lo);
} else {
crc.process((byte) value);
}
break;
case CRC16.CRCINIRES:
crc.reset(value & 0xffff);
break;
}
}
/**
* Registers are read
*/
public int read(int address, boolean word, long cycles) {
switch (address - offset) {
case CRC16.CRCINIRES:
return crc.getCRC();
case CRC16.CRCRESR:
return crc.getCrcRb();
}
return 0;
}
public void interruptServiced(int vector) {
}
}

View File

@ -0,0 +1,690 @@
package com.tado.mspsim.core;
import java.util.Calendar;
import java.util.GregorianCalendar;
import se.sics.mspsim.core.IOUnit;
import se.sics.mspsim.core.MSP430Core;
import se.sics.mspsim.core.TimeEvent;
import se.sics.mspsim.core.EmulationLogger.WarningType;
/**
* RTC module for the MSP430
*
* TODO: alarm mode not implemented
*
* @author Víctor Ariño <victor.arino@tado.com>
*/
public class RTC extends IOUnit {
/**
* Address and size for IO configuration
*/
public static final int OFFSET = 0x04A0;
public static final int SIZE = 0x0001A;
/**
* Interrupt vector
*/
public static final int RTC_VECTOR = 0x005C;
/* Peripheral registers */
private static final int RTCCTL01 = 0x0000;
private static final int RTCCTL23 = 0x0002;
private static final int RTCPS0CTL = 0x0008;
private static final int RTCPS1CTL = 0x000A;
private static final int RTCPS = 0x000C;
private static final int RTCIV = 0x000E;
private static final int RTCTIM0 = 0x0010;
private static final int RTCTIM1 = 0x0012;
private static final int RTCDATE = 0x0014;
private static final int RTCYEAR = 0x0016;
private static final int RTCAMINHR = 0x0018;
private static final int RTCADOWDAY = 0x001A;
/* RTCCTL01 Control Bits */
private static final int RTCBCD = 0x8000;
private static final int RTCHOLD = 0x4000;
private static final int RTCMODE = 0x2000;
private static final int RTCRDY = 0x1000;
private static final int RTCTEVIE = 0x0040;
private static final int RTCAIE = 0x0020;
private static final int RTCRDYIE = 0x0010;
private static final int RTCTEVIFG = 0x0004;
private static final int RTCAIFG = 0x0002;
private static final int RTCRDYIFG = 0x0001;
private static final int RTCSSEL__ACLK = 0x0000;
private static final int RTCSSEL__SMCLK = 0x0400;
private static final int RTCSSEL__RT1PS = 0x0800;
private static final int RTCOFIE = (1 << 7);
private static final int RTCOFIFG = (1 << 3);
private static final int RTCEVIFG = (1 << 2);
/* Possible types of RTC */
public static enum RtcType {
TYPE_A, TYPE_D,
}
private RtcType type = RtcType.TYPE_A;
private int rtcIntVector = RTC_VECTOR;
/**
* RTC peripheral for the MSP430
*
* @param cpu
* CPU core
*/
public RTC(MSP430Core cpu, int offset, RtcType r, int intVector) {
super("RTC", cpu, cpu.memory, offset);
type = r;
rtcIntVector = intVector;
DEBUG = true;
cal.setLenient(true);
}
/**
* Clear everything when reset
*/
public void reset(int type) {
oscFaultInterruptEnable = false;
eventInterruptEnable = false;
alarmInterruptEnable = false;
readyInterruptEnable = false;
oscFaultInterruptFlag = false;
eventInterruptFlag = false;
alarmInterruptFlag = false;
readyInterruptFlag = false;
}
/* RTCCTL0 */
private boolean oscFaultInterruptEnable = false;
private boolean eventInterruptEnable = false;
private boolean alarmInterruptEnable = false;
private boolean readyInterruptEnable = false;
private boolean oscFaultInterruptFlag = false;
private boolean eventInterruptFlag = false;
private boolean alarmInterruptFlag = false;
private boolean readyInterruptFlag = false;
/**
* Get the CTLO (byte) register
*
* @return
*/
private short getCTL0Reg() {
short ctl0 = 0;
ctl0 |= (oscFaultInterruptEnable ? 1 : 0) << 7;
ctl0 |= (eventInterruptEnable ? 1 : 0) << 6;
ctl0 |= (alarmInterruptEnable ? 1 : 0) << 5;
ctl0 |= (readyInterruptEnable ? 1 : 0) << 4;
ctl0 |= (oscFaultInterruptFlag ? 1 : 0) << 3;
ctl0 |= (eventInterruptFlag ? 1 : 0) << 2;
ctl0 |= (alarmInterruptFlag ? 1 : 0) << 1;
ctl0 |= (readyInterruptFlag ? 1 : 0) << 0;
return ctl0;
}
/**
* Get the CTL1 (byte) register
*
* @return
*/
private short getCTL1Reg() {
short ctl1 = 0;
ctl1 |= (formatBCD ? 1 : 0) << 7;
ctl1 |= (rtcHold ? 1 : 0) << 6;
ctl1 |= (modeCalendar ? 1 : 0) << 5;
ctl1 |= (rtcReady ? 1 : 0) << 4;
ctl1 |= (clockSource & 0x3) << 2;
ctl1 |= (rtcEvent & 0x3);
return ctl1;
}
/**
* Get the CTL01 (word) register
*
* @return
*/
private int getCTL01Reg() {
return getCTL1Reg() << 8 | getCTL0Reg();
}
/* RTCCTL1 */
private boolean formatBCD = false;
private boolean rtcHold = true;
private boolean modeCalendar = true;
private boolean rtcReady = true;
private int clockSource = 0;
private int rtcEvent = 0;
/**
* Handler of the 4 (byte) counter registers, for simplicity they can be
* collected in a single 32-bit variable.
*/
private long rtcCount = 0;
/**
* Period to increment the counters
*/
private double period = osc32KHzMs;
private static final float osc32KHzMs = 0.031f;
private int preScaler0Src = 0;
private int preScaler0Div = 0;
private boolean preScaler0Hold = false;
private int preScaler1Src = 0;
private int preScaler1Div = 0;
private boolean preScaler1Hold = false;
private static final int RT0SSEL = 0x4000;
private static final int RT0PSHOLD = 0x0100;
private static final int RT0PSIE = 0x0002;
private static final int RT0PSIFG = 0x0001;
private static final int RT0IP = 0x001C;
private static final int RT1SSEL = 0xC000;
private static final int RT1PSHOLD = 0x0100;
private static final int RT1PSIE = 0x0002;
private static final int RT1PSIFG = 0x0001;
private static final int RT1IP = 0x001C;
private int getPS0CTL() {
int ctl0 = 0;
ctl0 |= (preScaler0Src & 0x01) << 14;
ctl0 |= (preScaler0Div & 0x03) << 11;
ctl0 |= (preScaler0Hold ? 1 : 0) << 8;
return ctl0;
}
private int getPS1CTL() {
int ctl1 = 0;
ctl1 |= (preScaler1Src & 0x03) << 14;
ctl1 |= (preScaler1Div & 0x03) << 11;
ctl1 |= (preScaler1Hold ? 1 : 0) << 8;
return ctl1;
}
/**
* In calendar mode, use a gregorian calendar
*/
private GregorianCalendar cal = new GregorianCalendar(0, 1, 1, 0, 0, 0);
/**
* Timer to generate the interrupts and handle the calendar
*/
private TimeEvent rtcTimer = new TimeEvent(0) {
public void execute(long t) {
if (!rtcHold) {
updateCounters();
reSchedule();
}
if (getIV() > 0) {
cpu.flagInterrupt(rtcIntVector, RTC.this, true);
}
}
/**
* Re-Schedule the timer event
*/
private void reSchedule() {
// cpu.scheduleTimeEventMillis(this, period);
rtcInit();
}
/**
* Update the counters or the calendar, depending on the mode
*/
private void updateCounters() {
if (modeCalendar) {
/* In calendar mode it is updated every second */
cal.add(Calendar.SECOND, 1);
/* Track the changes */
int sec = cal.get(Calendar.SECOND);
int min = cal.get(Calendar.MINUTE);
int hour = cal.get(Calendar.HOUR_OF_DAY);
if (sec == 0) {
if (rtcEvent == 0) { // minute
generateInterrupt();
} else if (rtcEvent == 1 && min == 0) { // hour
generateInterrupt();
} else if (rtcEvent == 2 && hour == 0 && min == 0) { // day
generateInterrupt();
} else if (rtcEvent == 3 && hour == 12 && min == 0) { // day
generateInterrupt();
}
}
/*
* Generate interrupts when time successfully increased and there's
* no other interrupt
*/
if (readyInterruptEnable && modeCalendar) {
readyInterruptFlag = true;
}
} else {
rtcCount += 1;
long overflow = ((1L << ((rtcEvent + 1) * 8)));
overflow -= 1;
if ((rtcCount & overflow) == 0) {
generateInterrupt();
}
if (rtcCount >= (1L << 32)) {
rtcCount = 0;
}
}
}
/**
* Trigger a microprocessor interrupt
*/
private void generateInterrupt() {
eventInterruptFlag = true;
}
};
private double getPreScalerFreq() {
double freqSrc = 0;
if (preScaler1Src > 1) {
/* From preScaler 0 */
if (preScaler0Src == 0) {
freqSrc = cpu.getAclkFrq();
} else {
freqSrc = cpu.getSmclkFrq();
}
int div = (0x2 << preScaler0Div);
freqSrc /= div;
} else if (preScaler1Src == 0) {
freqSrc = cpu.getAclkFrq();
} else {
freqSrc = cpu.getSmclkFrq();
}
int div = (0x2 << preScaler1Div);
freqSrc /= div;
return freqSrc;
}
/**
* Initialize the RTC clock. This is called when the hold bit is released
*/
private void rtcInit() {
double freqSrc = 1f;
if (modeCalendar) {
/* In this mode we can set the counter to 1 second */
freqSrc = 1f; // 1Hz = ~1s
} else {
if (type == RtcType.TYPE_A) {
/* For the RTC_A the sources are aclk, smclk, rt1ps */
if (clockSource == 0) {
freqSrc = cpu.getAclkFrq();
} else if (clockSource == 1) {
freqSrc = cpu.getSmclkFrq();
} else {
freqSrc = getPreScalerFreq();
}
} else {
/* For the RTC_D the sources are 32-khz, rt1ps */
if (clockSource <= 1) {
freqSrc = 32000;
} else {
freqSrc = getPreScalerFreq();
}
}
}
period = 1000f / freqSrc;
cpu.scheduleTimeEventMillis(rtcTimer, period);
}
/**
* Reset the calendar to 01.01.0000
*/
private void resetCalendar() {
cal = new GregorianCalendar(0, 1, 1, 0, 0, 0);
}
/**
* Get the interrupt vector
*
* @return
*/
private int getIV() {
int iv = 0;
iv |= (readyInterruptFlag ? 1 : 0) << 1;
iv |= (eventInterruptFlag ? 1 : 0) << 2;
iv |= (alarmInterruptFlag ? 1 : 0) << 3;
return iv;
}
/**
* Given a calendar field, get the BCD or hex representation
*
* @param calField
* Calendar.XXXX field
* @return the formated field
*/
private int formatField(int calField) {
int f = cal.get(calField);
/* Day of week is 0-6 in uC and 1-7 in Java */
if (calField == Calendar.DAY_OF_WEEK) {
f -= 1;
}
int res = 0;
int base = 0;
if (formatBCD) {
base = 10;
} else {
base = 16;
}
for (int i = 0; f > 0; i++) {
int v = f % base;
f = f / base;
res |= (v << i * 4);
}
return res;
}
/**
* Parses a calendar input
*
* This processes the registers written in BCD or hex format
*
* @param calField
* the calendar field to parse
* @param value
* the input value
*/
private void parseCalReg(int calField, int value) {
int res = 0;
int factor = 1;
int base = 0;
if (formatBCD) {
base = 10;
} else {
base = 16;
}
/* Day of week is 0-6 in uC and 1-7 in Java */
if (calField == Calendar.DAY_OF_WEEK) {
value += 1;
}
while (value > 0) {
int v = value & 0x0f;
res += (v * factor);
value >>= 4;
factor *= base;
}
cal.set(calField, res);
cal.get(calField); // Until get is not done the fields are not set
}
/**
* Clear the highest priority interrupt flag
*/
private void clearHighestInterrupt() {
if (readyInterruptFlag) {
readyInterruptFlag = false;
} else if (eventInterruptFlag) {
eventInterruptFlag = false;
} else if (alarmInterruptFlag) {
alarmInterruptFlag = false;
}
if (getIV() > 0) {
cpu.flagInterrupt(rtcIntVector, this, true);
}
}
/**
* The registers are written
*/
public void write(int address, int value, boolean word, long cycles) {
/*
* XXX: this assumes always word access
*/
if (!word) {
logw(WarningType.MISALIGNED_WRITE, "byte access not implemented");
}
int lo = (value) & 0xff; // low byte
int hi = (value >> 8) & 0xff; // high byte
switch (address - offset) {
case RTCCTL01:
oscFaultInterruptEnable = ((value & RTCOFIE) == RTCOFIE);
eventInterruptEnable = ((value & RTCTEVIE) == RTCTEVIE);
alarmInterruptEnable = ((value & RTCAIE) == RTCAIE);
readyInterruptEnable = ((value & RTCRDYIE) == RTCRDYIE);
oscFaultInterruptFlag = ((value & RTCOFIFG) == RTCOFIFG);
eventInterruptFlag = ((value & RTCEVIFG) == RTCEVIFG);
alarmInterruptFlag = ((value & RTCAIFG) == RTCAIFG);
readyInterruptFlag = ((value & RTCRDYIFG) == RTCRDYIFG);
rtcHold = ((value & RTCHOLD) == RTCHOLD);
rtcReady = ((value & RTCRDY) == RTCRDY);
clockSource = (value & 0x0C00) >> 10;
rtcEvent = (value & 0x0300) >> 8;
/*
* Either the BCD has changed and is in mode calendar or the mode
* calendar has been enabled
*/
if ((modeCalendar && formatBCD != ((value & RTCBCD) == RTCBCD))
|| (modeCalendar == false && (value & RTCMODE) == RTCMODE)) {
/*
* Changing this bit clears seconds, minutes, hours, day of week,
* and year to 0 and sets day of month and month to 1. The real-time
* clock registers must be set by software afterwards.
*/
resetCalendar();
}
modeCalendar = ((value & RTCMODE) == RTCMODE);
formatBCD = ((value & RTCBCD) == RTCBCD);
/* Initialize the RTC or stop it */
if (!rtcHold) {
rtcInit();
}
break;
case RTCPS0CTL:
preScaler0Src = ((value & 0x4000) >> 14);
preScaler0Div = ((value & 0x3800) >> 11);
preScaler0Hold = ((value & RT0PSHOLD) == RT0PSHOLD);
if (((value & (RT0PSIE | RT0PSIFG | RT0IP)) > 0)) {
logNotImplemented("prescaling interrupts");
}
break;
case RTCPS1CTL:
preScaler1Src = ((value & 0xC000) >> 14);
preScaler1Div = ((value & 0x3800) >> 11);
preScaler1Hold = ((value & RT1PSHOLD) == RT1PSHOLD);
if (((value & (RT1PSIE | RT1PSIFG | RT1IP)) > 0)) {
logNotImplemented("prescaling interrupts");
}
break;
case RTCCTL23:
logNotImplemented("Calibration");
break;
case RTCPS:
logNotImplemented("prescaling counter");
break;
case RTCTIM0: // RTCNT12
if (modeCalendar) {
parseCalReg(Calendar.SECOND, lo);
parseCalReg(Calendar.MINUTE, hi);
} else {
rtcCount &= 0xffff0000;
rtcCount |= value;
}
break;
case RTCTIM1: // RTCNT34
if (modeCalendar) {
parseCalReg(Calendar.HOUR_OF_DAY, lo);
parseCalReg(Calendar.DAY_OF_WEEK, hi);
} else {
rtcCount &= 0x0000ffff;
rtcCount |= (long) (value << 16);
}
break;
case RTCDATE:
if (modeCalendar) {
parseCalReg(Calendar.DAY_OF_MONTH, lo);
parseCalReg(Calendar.MONTH, hi);
}
break;
case RTCYEAR:
if (modeCalendar) {
parseCalReg(Calendar.YEAR, value);
}
break;
case RTCIV:
/*
* Any access, read or write, of the RTCIV register automatically
* resets the highest-pending interrupt flag. If another interrupt flag
* is set, another interrupt is immediately generated after servicing
* the initial interrupt. In addition, all flags can be cleared via
* software.
*/
clearHighestInterrupt();
default:
logNotImplemented("register: " + address);
}
}
/**
* Registers are read
*/
public int read(int address, boolean word, long cycles) {
if (!word) {
logw(WarningType.MISALIGNED_READ, "byte access not implemented");
}
switch (address - offset) {
case RTCCTL01:
if (word) {
return getCTL01Reg();
} else {
return getCTL0Reg();
}
case RTCCTL23:
logNotImplemented("calibration");
break;
case RTCPS:
logNotImplemented("prescaling coutner");
break;
case RTCPS0CTL:
return getPS0CTL();
case RTCPS1CTL:
return getPS1CTL();
case RTCIV:
/*
* Any access, read or write, of the RTCIV register automatically
* resets the highest-pending interrupt flag. If another interrupt flag
* is set, another interrupt is immediately generated after servicing
* the initial interrupt. In addition, all flags can be cleared via
* software.
*/
int tmp = getIV();
clearHighestInterrupt();
return tmp;
case RTCTIM0: // RTCNT12
if (modeCalendar) {
return formatField(Calendar.MINUTE) << 8
| formatField(Calendar.SECOND);
} else {
return (int) (rtcCount & 0xffff);
}
case RTCTIM1: // RTCNT34
if (modeCalendar) {
return formatField(Calendar.DAY_OF_WEEK) << 8
| formatField(Calendar.HOUR_OF_DAY);
} else {
return (int) ((rtcCount >> 16) & 0xffff);
}
case RTCDATE:
if (modeCalendar) {
return formatField(Calendar.MONTH) << 8
| formatField(Calendar.DAY_OF_MONTH);
}
break;
case RTCYEAR:
if (modeCalendar) {
return formatField(Calendar.YEAR);
}
break;
default:
logNotImplemented("register: " + address);
}
return 0;
}
public void interruptServiced(int vector) {
/*
* NOTE this function is called BEFORE the interrupt is actually
* serviced!!!
*/
if (vector == rtcIntVector) {
cpu.flagInterrupt(rtcIntVector, this, false);
}
}
/*
* The inherited log function is not working for whatever reason. A quick
* redefinition helps a lot while developing the module
*/
@Override
protected void log(String msg) {
if (DEBUG) {
System.out.println(msg);
}
}
/**
* Log using printf format
*
* @param format
* @param arguments
*/
protected void log(final String format, final Object... arguments) {
if (DEBUG) {
System.out.printf(format, arguments);
}
}
private void logNotImplemented(String feature) {
logw(WarningType.EMULATION_ERROR, feature + " is not implemented");
}
}