mirror of https://github.com/contiki-ng/mspsim
Merge pull request #15 from lebrush/peripherals
ADC12Plus, AES128, CRC16 and RTC peripheral implementation
This commit is contained in:
commit
d19bfb1493
|
|
@ -0,0 +1,440 @@
|
|||
/**
|
||||
* 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.
|
||||
*
|
||||
* -----------------------------------------------------------------
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/* Copyright (c) 2013, tado° GmbH. Munich, Germany.
|
||||
* 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.
|
||||
*
|
||||
* Author: Víctor Ariño <victor.arino@tado.com>
|
||||
*
|
||||
*/
|
||||
|
||||
package se.sics.mspsim.core;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
|
||||
/**
|
||||
* 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 Joakim Eriksson <joakime@sics.se>
|
||||
* @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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,452 @@
|
|||
/* Copyright (c) 2013, tado° GmbH. Munich, Germany.
|
||||
* 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.
|
||||
*
|
||||
* Author: Víctor Ariño <victor.arino@tado.com>
|
||||
*
|
||||
*/
|
||||
package se.sics.mspsim.core;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,250 @@
|
|||
/* Copyright (c) 2013, tado° GmbH. Munich, Germany.
|
||||
* 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.
|
||||
*
|
||||
* Author: Víctor Ariño <victor.arino@tado.com>
|
||||
*
|
||||
*/
|
||||
package se.sics.mspsim.core;
|
||||
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,718 @@
|
|||
/* Copyright (c) 2013, tado° GmbH. Munich, Germany.
|
||||
* 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.
|
||||
*
|
||||
* Author: Víctor Ariño <victor.arino@tado.com>
|
||||
*
|
||||
*/
|
||||
package se.sics.mspsim.core;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
|
||||
/**
|
||||
* 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");
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue