Emulation support for the TI CC430 sub-GHz SoC

This commit is contained in:
Adam Dunkels 2013-08-29 19:50:24 +02:00 committed by Niclas Finne
parent 5d60f2736c
commit f1e8246a1d
5 changed files with 554 additions and 0 deletions

BIN
images/cc430.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View File

@ -0,0 +1,166 @@
/**
* Copyright (c) 2011, 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.
*
*
* -----------------------------------------------------------------
*
* Author : Joakim Eriksson
*/
package se.sics.mspsim.config;
import java.util.ArrayList;
import se.sics.mspsim.core.ClockSystem;
import se.sics.mspsim.core.GenericUSCI;
import se.sics.mspsim.core.IOPort;
import se.sics.mspsim.core.IOUnit;
import se.sics.mspsim.core.MSP430Config;
import se.sics.mspsim.core.MSP430Core;
import se.sics.mspsim.core.Multiplier32;
import se.sics.mspsim.core.PMM;
import se.sics.mspsim.core.PMMDummy;
import se.sics.mspsim.core.SysReg;
import se.sics.mspsim.core.Timer;
import se.sics.mspsim.core.UnifiedClockSystem;
import se.sics.mspsim.util.Utils;
import se.sics.mspsim.core.RF1A;
public class CC430f5137Config extends MSP430Config {
// NOTE: this MCU also needs to configure
// - positions of all timers (A0, A1, B)
// - memory configuration
// -
private static final String portConfig[] = {
"P1=200,IN 00,OUT 02,DIR 04,REN 06,DS 08,SEL 0A,IV_L 0E,IV_H 0F,IES 18,IE 1A,IFG 1C",
"P2=200,IN 01,OUT 03,DIR 05,REN 07,DS 09,SEL 0B,IV_L 1E,IV_H 1F,IES 19,IE 1B,IFG 1D",
"P3=220,IN 00,OUT 02,DIR 04,REN 06,DS 08,SEL 0A",
"P4=220,IN 01,OUT 03,DIR 05,REN 07,DS 09,SEL 0B",
"P5=240,IN 00,OUT 02,DIR 04,REN 06,DS 08,SEL 0A",
"P6=240,IN 01,OUT 03,DIR 05,REN 07,DS 09,SEL 0B",
"P7=260,IN 00,OUT 02,DIR 04,REN 06,DS 08,SEL 0A",
"P8=260,IN 01,OUT 03,DIR 05,REN 07,DS 09,SEL 0B",
"P9=280,IN 00,OUT 02,DIR 04,REN 06,DS 08,SEL 0A",
"P:=280,IN 01,OUT 03,DIR 05,REN 07,DS 09,SEL 0B",
};
public CC430f5137Config() {
/* 64 vectors for the MSP430f54xx series */
maxInterruptVector = 63;
MSP430XArch = true;
flashControllerOffset = 0x140;
sfrOffset = 0x100;
/* configuration for the timers - need to set-up new source maps!!! */
/* XXX Adam: the ints after TimerConfig(X, Y) seem to be the interrupt vectors, i.e. i = (vector_addresss - 0xff80)/2 */
//#define TIMER1_A1_VECTOR (0x0064) /* 0xFFE4 Timer1_A3 CC1-2, TA1 */
//#define TIMER1_A0_VECTOR (0x0066) /* 0xFFE6 Timer1_A3 CC0 */
//#define DMA_VECTOR (0x0068) /* 0xFFE8 DMA */
//#define CC1101_VECTOR (0x006A) /* 0xFFEA CC1101 Radio Interface */
//#define TIMER0_A1_VECTOR (0x006C) /* 0xFFEC Timer0_A5 CC1-4, TA */
//#define TIMER0_A0_VECTOR (0x006E) /* 0xFFEE Timer0_A5 CC0 */
TimerConfig timerA0 = new TimerConfig(0x6e / 2, 0x6c / 2, 5, 0x340, Timer.TIMER_Bx149, "TimerA0", 0x340 + 0x2e);
TimerConfig timerA1 = new TimerConfig(0x66 / 2, 0x64 / 2, 3, 0x380, Timer.TIMER_Ax149, "TimerA1", 0x380 + 0x2e);
timerConfig = new TimerConfig[] {timerA0, timerA1};
uartConfig = new UARTConfig[] {
new UARTConfig("USCI A0", 57, 0x5c0),
new UARTConfig("USCI B0", 56, 0x5e0),
new UARTConfig("USCI A1", 46, 0x600),
new UARTConfig("USCI B1", 45, 0x620),
};
/* configure memory */
infoMemConfig(0x1800, 128 * 4);
ramConfig(0x1c00, 4 * 1024);
mainFlashConfig(0x8000, 32 * 1024);
ioMemSize(0x1000); /* 4 KB of IO Memory */
watchdogOffset = 0x15c;
// bsl, IO, etc at a later stage...
}
public int setup(MSP430Core cpu, ArrayList<IOUnit> ioUnits) {
Multiplier32 mp = new Multiplier32(cpu, cpu.memory, 0x4c0);
cpu.setIORange(0x4c0, 0x2e, mp);
/* this code should be slightly more generic... and be somewhere else... */
for (int i = 0, n = uartConfig.length; i < n; i++) {
GenericUSCI usci = new GenericUSCI(cpu, i, cpu.memory, this);
/* setup 0 - 1f as IO addresses */
cpu.setIORange(uartConfig[i].offset, 0x20, usci);
// System.out.println("Adding IOUnit USCI: " + usci.getName());
ioUnits.add(usci);
}
IOPort last = null;
ioUnits.add(last = IOPort.parseIOPort(cpu, 47, portConfig[0], last));
ioUnits.add(last = IOPort.parseIOPort(cpu, 42, portConfig[1], last));
for (int i = 2; i < portConfig.length; i++) {
ioUnits.add(last = IOPort.parseIOPort(cpu, 0, portConfig[i], last));
}
/* XXX: Stub IO units: Sysreg and PMM */
SysReg sysreg = new SysReg(cpu, cpu.memory);
cpu.setIORange(SysReg.ADDRESS, SysReg.SIZE, sysreg);
ioUnits.add(sysreg);
PMMDummy pmmDummy = new PMMDummy(cpu, cpu.memory, 0x110);
cpu.setIORange(0x110, PMMDummy.SIZE, pmmDummy);
ioUnits.add(pmmDummy);
PMM pmm = new PMM(cpu, cpu.memory, 0x120);
cpu.setIORange(0x120, PMM.SIZE, pmm);
ioUnits.add(pmm);
RF1A rf1a = new RF1A(cpu, cpu.memory);
cpu.setIORange(RF1A.ADDRESS, RF1A.SIZE, rf1a);
ioUnits.add(rf1a);
return portConfig.length + uartConfig.length;
}
public String getAddressAsString(int addr) {
return Utils.hex20(addr);
}
public ClockSystem createClockSystem(MSP430Core cpu, int[] memory, Timer[] timers) {
return new UnifiedClockSystem(cpu, memory, 0, timers);
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2012, Thingsquare.
* 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.
*/
package se.sics.mspsim.core;
public class PMMDummy extends IOUnit {
public static int SIZE = 0x10;
public PMMDummy(MSP430Core cpu, int[] memory, int address) {
super("PMMDummy", "PMMDummy", cpu, memory, address);
reset(0);
}
public void reset(int type) {
}
public void write(int address, int value, boolean word, long cycles) {
memory[address] = value;
}
public int read(int address, boolean word, long cycles) {
return memory[address];
}
public void interruptServiced(int vector) {
if (DEBUG) {
log(this.getName() + ": Interrupt services vector: " + vector);
}
}
}

View File

@ -0,0 +1,248 @@
/*
* Copyright (c) 2012, Thingsquare.
* 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.
*/
package se.sics.mspsim.core;
import se.sics.mspsim.chip.CC1101;
import se.sics.mspsim.chip.CC1101.GDOListener;
import se.sics.mspsim.core.IOPort.PinState;
public class RF1A extends IOUnit implements InterruptHandler {
private boolean DEBUG = true;
public static int ADDRESS = 0xf00;
public static int SIZE = 64;
private static final int RF1AIFCTL0 = 0x00;
private static final int RF1AIFCTL1 = 0x02;
private static final int RF1AIFCTL2 = 0x04;
private static final int RF1AIFERR = 0x06;
private static final int RF1AIFERRV = 0x0c;
private static final int RF1AIFIV = 0x0e;
private static final int RF1AINSTRW = 0x10;
private static final int RF1AINSTR1W = 0x12;
private static final int RF1AINSTR2B = 0x14;
private static final int RF1ADINW = 0x16;
private static final int RF1ADOUTB = 0x20;
private static final int RF1ASTATB = 0x21;
private static final int RF1ASTAT1W = 0x22;
private static final int RF1ASTAT2W = 0x24;
private static final int RF1ADOUT0W = 0x28;
private static final int RF1ADOUT1W = 0x2a;
private static final int RF1ADOUT2W = 0x2c;
private static final int RF1AIN = 0x30;
private static final int RF1AIFG = 0x32;
private static final int RF1AIES = 0x34;
private static final int RF1AIE = 0x36;
private static final int RF1AIV = 0x38;
private static final int RF1ARXFIFO = 0x3c;
private static final int RF1ATXFIFO = 0x3e;
private CC1101 cc1101;
private boolean interruptOnCC1101GDO0 = false;
private boolean gdo0IsHigh = false;
public RF1A(final MSP430Core cpu, int[] memory) {
super("RF1A", "RF1A", cpu, memory, ADDRESS);
cc1101 = new CC1101(cpu);
cc1101.setGDO0Listener(new GDOListener() {
public void event(PinState state) {
/* cc430f5137 datasheet p. 17 */
gdo0IsHigh = (state == PinState.HI);
cpu.flagInterrupt(53, RF1A.this, gdo0IsHigh);
}
});
reset(0);
}
public void reset(int type) {
/* Initial values */
memory[ADDRESS + 0x00] = 0x0000; /* RF1AIFCTL0 */
memory[ADDRESS + 0x02] = 0x0000; /* RF1AIFCTL1 */
memory[ADDRESS + 0x04] = 0x0000; /* RF1AIFCTL2 */
memory[ADDRESS + 0x06] = 0x0000; /* RF1AIFERR */
memory[ADDRESS + 0x0c] = 0x0000; /* RF1AIFERRV */
memory[ADDRESS + 0x0e] = 0x0000; /* RF1AIFIV */
memory[ADDRESS + 0x10] = 0x0000; /* RF1AINSTRW */
memory[ADDRESS + 0x12] = 0x0000; /* RF1AINSTR1W */
memory[ADDRESS + 0x14] = 0x0000; /* RF1AINSTR2B */
memory[ADDRESS + 0x16] = 0x0000; /* RF1ADINW */
memory[ADDRESS + 0x20] = 0x0000; /* RF1ASTAT0W */
memory[ADDRESS + 0x22] = 0x0000; /* RF1ASTAT1W */
memory[ADDRESS + 0x24] = 0x0000; /* RF1ASTAT2W */
memory[ADDRESS + 0x28] = 0x0000; /* RF1ADOUT0W */
memory[ADDRESS + 0x2a] = 0x0000; /* RF1ADOUT1W */
memory[ADDRESS + 0x2c] = 0x0000; /* RF1ADOUT2W */
memory[ADDRESS + 0x30] = 0x0000; /* RF1AIN */
memory[ADDRESS + 0x32] = 0x0000; /* RF1AIFG */
memory[ADDRESS + 0x34] = 0x0000; /* RF1AIES */
memory[ADDRESS + 0x36] = 0x0000; /* RF1AIE */
memory[ADDRESS + 0x38] = 0x0000; /* RF1AIV */
memory[ADDRESS + 0x3c] = 0x0000; /* RF1ARXFIFO */
memory[ADDRESS + 0x3e] = 0x0000; /* RF1ATXFIFO */
interruptOnCC1101GDO0 = false;
ioAddress = -1;
ioBurst = false;
ioRead = false;
ioWrite = false;
expectingDummyWrite = false;
}
private int ioAddress = -1;
private boolean ioBurst = false;
private boolean ioRead = false;
private boolean ioWrite = false;
private boolean expectingDummyWrite = false;
public void write(int address, int value, boolean word, long cycles) {
/*if (DEBUG) {
System.out.printf(this.getName() + ": Write to 0x%04x: 0x%04x\n", address, value);
}*/
if (address == ADDRESS + RF1AIE) {
/* Configure interrupts */
interruptOnCC1101GDO0 = (value & 1) != 0;
} else if (address == ADDRESS + RF1AINSTRW + 1) {
/* Store address. Read/write? Burst/single? */
expectingDummyWrite = false;
ioBurst = CC1101.spiIsBurst(value);
if (!ioBurst && CC1101.spiIsStrobe(value)) {
/* Execute strobe command */
cc1101.setLastInstructionWasRead(false); /* This is probably wrong! */
ioBurst = false;
ioRead = false;
ioWrite = false;
expectingDummyWrite = true;
cc1101.strobe(value & (~CC1101.SPI_READ_BIT) & (~CC1101.SPI_BURST_BIT));
} else {
/* Store address */
ioRead = CC1101.spiIsRead(value);
ioWrite = !ioRead;
cc1101.setLastInstructionWasRead(ioRead);
ioAddress = value & (~CC1101.SPI_READ_BIT) & (~CC1101.SPI_BURST_BIT);
if (DEBUG) {
if (ioAddress == CC1101.CC1101_RXFIFO) {
} else if (ioAddress == CC1101.CC1101_MARCSTATE) {
} else if (ioAddress == CC1101.CC1101_RXBYTES) {
} else if (ioAddress == CC1101.CC1101_TXBYTES) {
} else {
System.out.printf("IO address 0x%04x (" + (ioRead?"read":"write") + ", " + (ioBurst?"burst":"single") + ")\n", ioAddress);
}
}
if (ioRead) {
expectingDummyWrite = true;
}
}
} else if (address == ADDRESS + RF1AINSTRW) {
if (expectingDummyWrite) {
/* Ignore dummy write */
expectingDummyWrite = false;
return;
}
if (!ioRead && !ioWrite) {
System.out.printf("Error: Unknown data mode for write 0x%04x\n", address);
return;
} else if (ioRead) {
System.out.printf("Error: writing in read mode: 0x%04x\n", address);
return;
}
/* Write CC1101 register */
cc1101.setReg(ioAddress, value);
if (ioBurst) {
if (ioAddress != CC1101.CC1101_TXFIFO && ioAddress != CC1101.CC1101_PATABLE) {
ioAddress++;
}
} else {
ioWrite = false;
}
}
}
public int read(int address, boolean word, long cycles) {
/*if (DEBUG) {
System.out.printf(this.getName() + ": Read from 0x%04x\n", address);
}*/
if (address == ADDRESS + RF1AIV) {
if (interruptOnCC1101GDO0 && gdo0IsHigh) {
return 2; /* RF1AIV_RFIFG0 */
}
return 0;
} else if (address == ADDRESS + RF1AIFCTL1) {
/* XXX TODO: CTL1 always return 0xFF */
return 0xff;
} else if (address == ADDRESS + RF1ASTAT1W + 1) {
/* Marcstate */
return cc1101.getMarcstate();
} else if (!word && address == ADDRESS + RF1ASTATB) {
/* Status byte */
return cc1101.getStatusByte();
} else if (address == ADDRESS + RF1ADOUTB) {
/* Return last received byte from emulated SPI interface */
if (!ioRead && !ioWrite) {
System.out.printf("Error: Unknown data mode for read 0x%04x\n", address);
return -1;
} else if (ioWrite) {
System.out.printf("Error: reading in write mode: 0x%04x\n", address);
return -1;
}
/* Read CC1101 register */
int val = cc1101.getReg(ioAddress);
if (ioBurst) {
if (ioAddress != CC1101.CC1101_TXFIFO && ioAddress != CC1101.CC1101_PATABLE) {
ioAddress++;
}
} else {
ioRead = false;
}
return val;
}
return -1;
}
public void interruptServiced(int vector) {
if (DEBUG) {
System.out.println(this.getName() + ": Interrupt services vector: " + vector);
}
}
}

View File

@ -0,0 +1,84 @@
package se.sics.mspsim.platform.ti;
import java.io.IOException;
import se.sics.mspsim.config.CC430f5137Config;
import se.sics.mspsim.core.IOPort;
import se.sics.mspsim.core.IOUnit;
import se.sics.mspsim.core.PortListener;
import se.sics.mspsim.core.USARTListener;
import se.sics.mspsim.core.USARTSource;
import se.sics.mspsim.platform.GenericNode;
import se.sics.mspsim.ui.SerialMon;
import se.sics.mspsim.util.ArgumentManager;
public class CC430Node extends GenericNode implements PortListener, USARTListener {
IOPort port1;
IOPort port3;
IOPort port4;
IOPort port5;
IOPort port7;
IOPort port8;
public CC430Node() {
/* TODO XXX MSP430F5438 */
super("CC430", new CC430f5137Config());
}
public void dataReceived(USARTSource source, int data) {
}
public void portWrite(IOPort source, int data) {
}
private void setupNodePorts() {
port1 = cpu.getIOUnit(IOPort.class, "P1");
port1.addPortListener(this);
port3 = cpu.getIOUnit(IOPort.class, "P3");
port3.addPortListener(this);
port4 = cpu.getIOUnit(IOPort.class, "P4");
port4.addPortListener(this);
port5 = cpu.getIOUnit(IOPort.class, "P5");
port5.addPortListener(this);
port7 = cpu.getIOUnit(IOPort.class, "P7");
port7.addPortListener(this);
port8 = cpu.getIOUnit(IOPort.class, "P8");
port8.addPortListener(this);
IOUnit usart0 = cpu.getIOUnit("USCI B0");
if (usart0 instanceof USARTSource) {
registry.registerComponent("serialio0", usart0);
}
IOUnit usart = cpu.getIOUnit("USCI A0");
if (usart instanceof USARTSource) {
registry.registerComponent("serialio", usart);
}
}
public void setupNode() {
setupNodePorts();
if (!config.getPropertyAsBoolean("nogui", true)) {
// Add some windows for listening to serial output
IOUnit usart = cpu.getIOUnit("USCI A0");
if (usart instanceof USARTSource) {
SerialMon serial = new SerialMon((USARTSource)usart, "USCI A0 Port Output");
registry.registerComponent("serialgui", serial);
}
}
}
public int getModeMax() {
return 0;
}
public static void main(String[] args) throws IOException {
CC430Node node = new CC430Node();
ArgumentManager config = new ArgumentManager();
config.handleArguments(args);
node.setupArgs(config);
}
}