From 9bd6a1d03de71eb92d23dbd95c8168236df2817d Mon Sep 17 00:00:00 2001 From: Adam Dunkels Date: Thu, 29 Aug 2013 19:48:54 +0200 Subject: [PATCH 01/14] Emulation support for the TI CC1101 sub-GHz transceiver --- se/sics/mspsim/chip/CC1101.java | 827 ++++++++++++++++++++++++++++++++ 1 file changed, 827 insertions(+) create mode 100644 se/sics/mspsim/chip/CC1101.java diff --git a/se/sics/mspsim/chip/CC1101.java b/se/sics/mspsim/chip/CC1101.java new file mode 100644 index 0000000..fa8d85d --- /dev/null +++ b/se/sics/mspsim/chip/CC1101.java @@ -0,0 +1,827 @@ +/* + * 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.chip; +import java.util.ArrayList; +import java.util.List; + +import se.sics.mspsim.core.IOPort; +import se.sics.mspsim.core.IOPort.PinState; +import se.sics.mspsim.core.MSP430Core; +import se.sics.mspsim.core.TimeEvent; +import se.sics.mspsim.core.USARTListener; +import se.sics.mspsim.core.USARTSource; + +public class CC1101 extends Radio802154 implements USARTListener { + protected boolean DEBUG = true; + + /* cc1101-const.h: Configuration registers */ + public static final int CC1101_IOCFG1 = 0x01; + public static final int CC1101_IOCFG0 = 0x02; + public static final int CC1101_FIFOTHR = 0x03; + public static final int CC1101_SYNC1 = 0x04; + public static final int CC1101_SYNC0 = 0x05; + public static final int CC1101_PKTLEN = 0x06; + public static final int CC1101_PKTCTRL1 = 0x07; + public static final int CC1101_PKTCTRL0 = 0x08; + public static final int CC1101_ADDR = 0x09; + public static final int CC1101_CHANNR = 0x0A; + public static final int CC1101_FSCTRL1 = 0x0B; + public static final int CC1101_FSCTRL0 = 0x0C; + public static final int CC1101_FREQ2 = 0x0D; + public static final int CC1101_FREQ1 = 0x0E; + public static final int CC1101_FREQ0 = 0x0F; + public static final int CC1101_MDMCFG4 = 0x10; + public static final int CC1101_MDMCFG3 = 0x11; + public static final int CC1101_MDMCFG2 = 0x12; + public static final int CC1101_MDMCFG1 = 0x13; + public static final int CC1101_MDMCFG0 = 0x14; + public static final int CC1101_DEVIATN = 0x15; + public static final int CC1101_MCSM2 = 0x16; + public static final int CC1101_MCSM1 = 0x17; + public static final int CC1101_MCSM0 = 0x18; + public static final int CC1101_FOCCFG = 0x19; + public static final int CC1101_BSCFG = 0x1A; + public static final int CC1101_AGCCTRL2 = 0x1B; + public static final int CC1101_AGCCTRL1 = 0x1C; + public static final int CC1101_AGCCTRL0 = 0x1D; + public static final int CC1101_WOREVT1 = 0x1E; + public static final int CC1101_WOREVT0 = 0x1F; + public static final int CC1101_WORCTRL = 0x20; + public static final int CC1101_FREND1 = 0x21; + public static final int CC1101_FREND0 = 0x22; + public static final int CC1101_FSCAL3 = 0x23; + public static final int CC1101_FSCAL2 = 0x24; + public static final int CC1101_FSCAL1 = 0x25; + public static final int CC1101_FSCAL0 = 0x26; + public static final int CC1101_RCCTRL1 = 0x27; + public static final int CC1101_RCCTRL0 = 0x28; + public static final int CC1101_FSTEST = 0x29; + public static final int CC1101_PTEST = 0x2A; + public static final int CC1101_AGCTEST = 0x2B; + public static final int CC1101_TEST2 = 0x2C; + public static final int CC1101_TEST1 = 0x2D; + public static final int CC1101_TEST0 = 0x2E; + public static final int CC1101_PARTNUM = 0x30; + public static final int CC1101_VERSION = 0x31; + public static final int CC1101_FREQEST = 0x32; + public static final int CC1101_LQI = 0x33; + public static final int CC1101_RSSI = 0x34; + public static final int CC1101_MARCSTATE = 0x35; + public static final int CC1101_WORTIME1 = 0x36; + public static final int CC1101_WORTIME0 = 0x37; + public static final int CC1101_PKTSTATUS = 0x38; + public static final int CC1101_VCO_VC_DAC = 0x39; + public static final int CC1101_TXBYTES = 0x3A; + public static final int CC1101_RXBYTES = 0x3B; + public static final int CC1101_RCCTRL1_STATUS = 0x3C; + public static final int CC1101_RCCTRL0_STATUS = 0x3D; + public static final int CC1101_PATABLE = 0x3E; + public static final int CC1101_PA_M30 = 0x03; + public static final int CC1101_PA_M20 = 0x0E; + public static final int CC1101_PA_M15 = 0x1E; + public static final int CC1101_PA_M10 = 0x27; + public static final int CC1101_PA_M6 = 0x38; + public static final int CC1101_PA_0 = 0x8E; + public static final int CC1101_PA_5 = 0x84; + public static final int CC1101_PA_7 = 0xCC; + public static final int CC1101_PA_10 = 0xC3; + public static final int CC1101_PA_11 = 0xC0; + + public final static int CC1101_TXFIFO = 0x3F; + public final static int CC1101_RXFIFO = 0x3F; + + /* Strobe commands */ + public static final int CC1101_SRES = 0x30; + public static final int CC1101_SFSTXON = 0x31; + public static final int CC1101_SXOFF = 0x32; + public static final int CC1101_SCAL = 0x33; + public static final int CC1101_SRX = 0x34; + public static final int CC1101_STX = 0x35; + public static final int CC1101_SIDLE = 0x36; + public static final int CC1101_SAFC = 0x37; + public static final int CC1101_SWOR = 0x38; + public static final int CC1101_SPWD = 0x39; + public static final int CC1101_SFRX = 0x3A; + public static final int CC1101_SFTX = 0x3B; + public static final int CC1101_SWORRST = 0x3C; + public static final int CC1101_SNOP = 0x3D; + + public static enum CC1101RadioState { + CC1101_STATE_SLEEP(0x00)/* 0 */, + CC1101_STATE_IDLE(0x01), + CC1101_STATE_XOFF(0x02), + CC1101_STATE_VCOON_MC(0x03), + CC1101_STATE_REGON_MC(0x04), + CC1101_STATE_MANCAL(0x05), + CC1101_STATE_VCOON(0x06), + CC1101_STATE_REGON(0x07), + CC1101_STATE_STARTCAL(0x08), + CC1101_STATE_BWBOOST(0x09), + CC1101_STATE_FS_LOCK(0x0A), + CC1101_STATE_IFADCON(0x0B), + CC1101_STATE_ENDCAL(0x0C), + CC1101_STATE_RX(0x0D) /* 13 */, + CC1101_STATE_RX_END(0x0E), + CC1101_STATE_RX_RST(0x0F), + CC1101_STATE_TXRX_SWITCH(0x10), + CC1101_STATE_RXFIFO_OVERFLOW(0x11), + CC1101_STATE_FSTXON(0x12), + CC1101_STATE_TX(0x13), + CC1101_STATE_TX_END(0x14), + CC1101_STATE_RXTX_SWITCH(0x15), + CC1101_STATE_TXFIFO_UNDERFLOW(0x16); + + private final int state; + + CC1101RadioState(int stateNo) { + state = stateNo; + } + + public int getStateAsInt() { + return state | (0b10 << 5); /* TODO 2 pin state not implemented */ + } + }; + private TimeEvent sendEvent = new TimeEvent(0, "CC1101 Send") { + public void execute(long t) { + txNext(); + } + }; + + public final static double SYMBOL_PERIOD = 0.016; /* TODO XXX 16 us */ + public final static double FREQUENCY_CHANNEL_0 = 902; /* MHz */ + public final static double FREQUENCY_CHANNEL_WIDTH = 0.125; /* MHz */ + + public final static int CCA_THRESHOLD = -95; + + private StateListener stateListener = null; + private ReceiverListener receiverListener = null; + + private int currentRssiReg = 0; + + private CC1101RadioState state = null; + + protected List txfifo = new ArrayList(); + protected List rxfifo = new ArrayList(); + + protected int[] registers = new int[64]; + protected int[] memory = new int[512]; + + private boolean chipSelect; + + private IOPort gdo0Port = null; + private int gdo0Pin = -1; + private IOPort gdo2Port = null; + private int gdo2Pin = -1; + + private int channel = 0; + + public CC1101(MSP430Core cpu) { + super("CC1101", "Radio", cpu); + reset(); + } + + public void log(String str) { + if (DEBUG) { + System.out.println(str); + } + } + + public void strobe(int cmd) { + switch (cmd) { + case CC1101_SRES: + log("CC1101_SRES not implemented"); + reset(); + break; + + case CC1101_SFSTXON: + log("CC1101_SFSTXON not implemented"); + break; + + case CC1101_SXOFF: + log("CC1101_SXOFF not implemented"); + break; + + case CC1101_SCAL: + setState(CC1101RadioState.CC1101_STATE_IDLE); + break; + + case CC1101_SRX: + setState(CC1101RadioState.CC1101_STATE_RX); + break; + + case CC1101_STX: + int len = (int) (0xff&txfifo.get(0)); + txFooterCountdown = 1 + len + 1/*len*/; + if (DEBUG) { + System.out.println("TX started: len = " + len + ", txFooterCountdown = " + txFooterCountdown); + } + txNext(); + setState(CC1101RadioState.CC1101_STATE_TX); + break; + + case CC1101_SIDLE: + setState(CC1101RadioState.CC1101_STATE_IDLE); + break; + + case CC1101_SAFC: + log("CC1101_SAFC not implemented"); + break; + + case CC1101_SWOR: + log("CC1101_SWOR not implemented"); + break; + + case CC1101_SPWD: + log("CC1101_SPWD almost implemented"); + /* TODO XXX + * Wait until CS is de-asserted. (We should at least wait + * receiving or transmitting.)*/ + setState(CC1101RadioState.CC1101_STATE_IDLE); + break; + + case CC1101_SFRX: + rxfifo.clear(); + rxExpectedLen = -1; + rxGotSynchByte = false; + setGDO0(false); + /*printRXFIFO();*/ + break; + + case CC1101_SFTX: + txfifo.clear(); + txFooterCountdown = -1; + /*printTXFIFO();*/ + break; + + case CC1101_SWORRST: + log("CC1101_SWORRST not implemented"); + break; + + case CC1101_SNOP: + log("CC1101_SNOP not implemented"); + break; + + default: + System.out.printf("strobe(0x%02x)\n", cmd); + break; + } + } + + public int getLQI() { + return 0; /* TODO */ + }; + + public void setLQI(int lqi) { + } + + /* SPI */ + public final static int SPI_READ_BIT = 0x80; + public final static int SPI_BURST_BIT = 0x40; + + private boolean spiBurstMode = false; + private boolean spiReadMode = false; + private boolean spiGotAddress = false; + private int spiAddress; + + public static boolean spiIsBurst(int data) { + return (data & SPI_BURST_BIT) == SPI_BURST_BIT; + } + + public static boolean spiIsRead(int data) { + return (data & SPI_READ_BIT) == SPI_READ_BIT; + } + + public static boolean spiIsStrobe(int data) { + data = data & 0x3f; + return data >= 0x30 && data <= 0x3d; + } + + private void spiResetState() { + spiBurstMode = false; + spiReadMode = false; + spiGotAddress = false; + spiAddress = 0xFF; + } + + public void dataReceived(USARTSource source, int data) { + if (spiGotAddress) { + if (!spiBurstMode) { + /* Single access mode */ + if (spiReadMode) { + source.byteReceived(getReg(spiAddress)); + } else { + source.byteReceived(setReg(spiAddress, data)); + } + spiResetState(); + } else { + /* Burst mode */ + if (spiReadMode) { + source.byteReceived(getReg(spiAddress)); + } else { + source.byteReceived(setReg(spiAddress, data)); + } + + if (spiAddress != CC1101_TXFIFO && spiAddress != CC1101_PATABLE) { + spiAddress++; + } + } + return; + } + + /* Read/write. Burst/single. */ + spiBurstMode = spiIsBurst(data); + spiReadMode = spiIsRead(data); + + /* Is this a strobe command? */ + if (!spiBurstMode && spiIsStrobe(data)) { + /* Strobe command */ + strobe(data); + source.byteReceived(0); + spiResetState(); + return; + } + + spiAddress = data & 0x3f; + spiGotAddress = true; + + /* Return MARCSTATE */ + source.byteReceived(getMarcstate()); + return; + } + public int setReg(int address, int data) { + switch (address) { + case CC1101_TXFIFO: + txfifo.add((byte) data); + /*printTXFIFO();*/ + return txfifo.size(); + case CC1101_CHANNR: + channel = data; + if (channelListener != null) { + channelListener.channelChanged(channel); + } + return 0; + } + + log(String.format("setReg(0x%02x) 0x%02x", address, data)); + int oldValue = registers[address]; + registers[address] = data; + configurationChanged(address, oldValue, data); + return oldValue; + } + public int getReg(int address) { + /* MSP430Core.profiler.printStackTrace(System.out); */ + switch (address) { + case CC1101_MARCSTATE: + return getMarcstate(); + case CC1101_RXBYTES: + /*log("getReg(CC1101_RXBYTES) " + rxfifo.size());*/ + return rxfifo.size(); + case CC1101_TXBYTES: + /*log("getReg(CC1101_TXBYTES) " + txfifo.size());*/ + return txfifo.size(); + case CC1101_PKTSTATUS: + int status; + final int CCA_BIT = (1<<4); + if(currentRssiReg < CCA_THRESHOLD) { + status = CCA_BIT; + } else { + status = 0; + } + return status; + case CC1101_RSSI: + return currentRssiReg; + case CC1101_RXFIFO: + if (rxfifo.size() > 0) { + int ret = (int) rxfifo.remove(0); + /*printRXFIFO();*/ + return ret; + } + System.err.println("Warning: reading from empty RXFIFO"); + return -1; + } + + log(String.format("getReg(0x%02x) 0x%02x", address, registers[address])); + return registers[address]; + } + + + + public boolean isReadyToReceive() { + /* TODO Implement me */ + if (getState() == CC1101RadioState.CC1101_STATE_IDLE) { + return false; + } + if (getState() == CC1101RadioState.CC1101_STATE_SLEEP) { + return false; + } + return true; + } + + /* txFooterCountdown: send CRC footer in these many bytes */ + protected int txFooterCountdown = -1; + + /* TX/RX states */ + public static final int NUM_PREAMBLE = 4; + public static final int NUM_SYNCH = 4; + public static final byte SYNCH_BYTE_LAST = (byte) 0x91; + private boolean txSentSynchByte = false; + private int txSendSynchByteCnt = 0; + private boolean txSentFirstCRC = false; + void txNext() { + if (txFooterCountdown < 0) { + System.out.println("Warning: Aborting transmit since txFooterCountdown=" + txFooterCountdown); + return; + } + + /* Send preamble and synch bytes */ + if (!txSentSynchByte) { + /* Send NUM_PREAMBLE preamble bytes */ + if (txSendSynchByteCnt < NUM_PREAMBLE) { + txSendSynchByteCnt++; + if (rfListener != null) { + rfListener.receivedByte((byte) (0xaa)); + } + cpu.scheduleTimeEventMillis(sendEvent, SYMBOL_PERIOD * 2); + return; + } + /* Send NUM_SYNCH-1 synch bytes */ + if (txSendSynchByteCnt < NUM_PREAMBLE + NUM_SYNCH - 1) { + txSendSynchByteCnt++; + if (rfListener != null) { + rfListener.receivedByte((byte) (SYNCH_BYTE_LAST + 1)); + } + cpu.scheduleTimeEventMillis(sendEvent, SYMBOL_PERIOD * 2); + return; + } + /* Send last synch byte */ + if (txSendSynchByteCnt < NUM_PREAMBLE + NUM_SYNCH) { + txSendSynchByteCnt++; + if (rfListener != null) { + rfListener.receivedByte((byte) (SYNCH_BYTE_LAST)); + } + cpu.scheduleTimeEventMillis(sendEvent, SYMBOL_PERIOD * 2); + + txSentSynchByte = true; + return; + } + + txSentFirstCRC = false; + } + + if (txSentFirstCRC) { + /* send second CRC byte */ + if (rfListener != null) { + rfListener.receivedByte((byte) (0xef)); + } + if (!txfifo.isEmpty()) { + System.out.println("Warning: TXFIFO not empty after sending CRC bytes"); + } + setStateRX(); + txSentSynchByte = false; + txSendSynchByteCnt = 0; + txSentFirstCRC = false; + return; + } + + txFooterCountdown--; + + if (txFooterCountdown == 0) { + /* countdown is zero, send first CRC byte */ + if (rfListener != null) { + rfListener.receivedByte((byte) (0xee)); + } + txSentFirstCRC = true; + cpu.scheduleTimeEventMillis(sendEvent, SYMBOL_PERIOD * 2); + return; + } + + /* Send payload byte (including first FIFO length byte) */ + if (txfifo.isEmpty()) { + System.out.println("Warning: TXFIFO is empty, sending zero-byte, txFooterCountdown=" + txFooterCountdown); + if (rfListener != null) { + rfListener.receivedByte((byte) 0); + } + cpu.scheduleTimeEventMillis(sendEvent, SYMBOL_PERIOD * 2); + setState(CC1101RadioState.CC1101_STATE_TXFIFO_UNDERFLOW); + return; + } + + if (rfListener != null) { + rfListener.receivedByte((byte) (txfifo.get(0).intValue())); + } + txfifo.remove(0); + cpu.scheduleTimeEventMillis(sendEvent, SYMBOL_PERIOD * 2); + + /*printTXFIFO();*/ + } + + public void setGDO0(IOPort port, int pin) { + gdo0Port = port; + gdo0Pin = pin; + } + public void setGDO0(boolean active) { + /* TODO Read active low/high configuration */ + + if (active) { + if (gdo0Port != null) { + gdo0Port.setPinState(gdo0Pin, IOPort.PinState.HI); + } + if (gdo0Listener != null) { + gdo0Listener.event(IOPort.PinState.HI); + } + } else { + if (gdo0Port != null) { + gdo0Port.setPinState(gdo0Pin, IOPort.PinState.LOW); + } + if (gdo0Listener != null) { + gdo0Listener.event(IOPort.PinState.LOW); + } + } + } + + public interface GDOListener { + public void event(PinState state); + } + private GDOListener gdo0Listener = null; + public void setGDO0Listener(GDOListener l) { + gdo0Listener = l; + } + + public void setGDO2(IOPort port, int pin) { + gdo2Port = port; + gdo2Pin = pin; + } + public void setGDO2(boolean active) { + if (gdo2Port == null) { + return; + } + gdo2Port.setPinState(gdo2Pin, active ? IOPort.PinState.LOW + : IOPort.PinState.HI); + } + + private void printRXFIFO() { + StringBuilder sb = new StringBuilder(); + sb.append(String.format("RXFIFO[%03d]: ", rxfifo.size())); + for (int i = 0; i < rxfifo.size(); i++) { + sb.append(String.format("%02x", rxfifo.get(i))); + } + log(sb.toString() + "\n"); + } + private void printTXFIFO() { + StringBuilder sb = new StringBuilder(); + sb.append(String.format("TXFIFO[%03d]: ", txfifo.size())); + for (int i = 0; i < txfifo.size(); i++) { + sb.append(String.format("%02x", txfifo.get(i))); + } + log(sb.toString() + "\n"); + } + + public CC1101RadioState getState() { + return state; + } + public interface StateListener { + public void newState(CC1101RadioState state); + } + public void setStateListener(StateListener listener) { + stateListener = listener; + } + public interface ReceiverListener { + public void newState(boolean on); + } + public void setReceiverListener(ReceiverListener listener) { + receiverListener = listener; + } + + boolean receiverOn = false; + boolean setState(CC1101RadioState newState) { + if (newState != CC1101RadioState.CC1101_STATE_IDLE + && newState != CC1101RadioState.CC1101_STATE_RX + && newState != CC1101RadioState.CC1101_STATE_TX) { + log("setState(" + newState + ")"); + } + + state = newState; + + /* Notify state listener */ + if (stateListener != null) { + stateListener.newState(state); + } + if (receiverListener != null && isReadyToReceive() != receiverOn) { + receiverOn = isReadyToReceive(); + receiverListener.newState(receiverOn); + } + return true; + } + + boolean rxGotSynchByte = false; + private int rxExpectedLen = -1; + public void receivedByte(byte data) { + if (!rxGotSynchByte) { + /* Await synch byte */ + if (data == SYNCH_BYTE_LAST) { + rxGotSynchByte = true; + setGDO0(true); + } + return; + } + + if (rxExpectedLen < 0) { + rxExpectedLen = 1/*len*/ + (int) data/*payload*/ + 2/*CRC*/; + } + + rxExpectedLen--; + if (rxExpectedLen == 0) { + setGDO0(false); + rxExpectedLen = -1; + rxGotSynchByte = false; + } + + rxfifo.add(data); + /*printRXFIFO();*/ + } + + public String info() { + return "CC1101 info: [not implemented]"; + } + + public int getRegister(int register) { + return registers[register]; + } + + public void setRegister(int register, int data) { + registers[register] = data; + } + + public void setRSSI(int power) { + currentRssiReg = power; + } + public int getRSSI() { + return currentRssiReg; + } + + public int getActiveFrequency() { + return (int) 0; /* Not implemented */ + } + + public int getActiveChannel() { + return channel; + } + + public int getOutputPowerIndicator() { + return 1; + } + + public double getFrequency() { + return 0; /* Not implemented */ + } + + public void notifyReset() { + super.notifyReset(); + setChipSelect(false); + } + + public int getOutputPower() { + return 1; + } + + public int getOutputPowerMax() { + return 1; + } + + public int getOutputPowerIndicatorMax() { + return 1; + } + + /***************************************************************************** + * Chip APIs + *****************************************************************************/ + + public int getModeMax() { + return 0; + } + + /* return data in register at the correct position */ + public int getConfiguration(int parameter) { + return registers[parameter]; + } + + public boolean getChipSelect() { + return chipSelect; + } + + public void setChipSelect(boolean select) { + chipSelect = select; + if (!chipSelect) { + spiResetState(); + } + + if (DEBUG) { + /*log("Chip select: " + chipSelect);*/ + } + } + + void setStateRX() { + setState(CC1101RadioState.CC1101_STATE_RX); + } + + void reset() { + setState(CC1101RadioState.CC1101_STATE_IDLE); + + registers[CC1101_PARTNUM] = 0; + registers[CC1101_VERSION] = 6; + } + + public int getMarcstate() { + return getState().getStateAsInt(); + } + + private boolean lastWasRead = false; + public void setLastInstructionWasRead(boolean wasRead) { + lastWasRead = wasRead; + } + + public int getStatusByte() { + int status = 0; + + /* Bit 7: RF ready */ + if (true) { + status += 0; /* XXX Always ready! */ + } + + /* Bit 4-6: simplified state */ + status = status << 3; + if (state == CC1101RadioState.CC1101_STATE_IDLE) { + status += 0; + } else if (state == CC1101RadioState.CC1101_STATE_RX || + state == CC1101RadioState.CC1101_STATE_RX_END || + state == CC1101RadioState.CC1101_STATE_RX_RST) { + status += 1; + } else if (state == CC1101RadioState.CC1101_STATE_TX || + state == CC1101RadioState.CC1101_STATE_TX_END) { + status += 2; + } else if (state == CC1101RadioState.CC1101_STATE_FSTXON) { + status += 3; + } else if (state == CC1101RadioState.CC1101_STATE_ENDCAL || + state == CC1101RadioState.CC1101_STATE_MANCAL || + state == CC1101RadioState.CC1101_STATE_STARTCAL) { + status += 4; + } else if (state == CC1101RadioState.CC1101_STATE_RXTX_SWITCH || + state == CC1101RadioState.CC1101_STATE_TXRX_SWITCH) { + status += 5; + } else if (state == CC1101RadioState.CC1101_STATE_RXFIFO_OVERFLOW) { + status += 6; + } else if (state == CC1101RadioState.CC1101_STATE_TXFIFO_UNDERFLOW) { + status += 7; + } + + /* Bit 0-3 */ + status = status << 4; + if (lastWasRead) { + /* Return available bytes in RXFIFO */ + int available = rxfifo.size(); + if (available > 15) { + status += 15; + } else { + status += available; + } + } else { + /* Return available bytes in TXFIFO */ + int available = 64 - txfifo.size(); + if (available > 15) { + status += 15; + } else { + status += available; + } + } + + return status; + } + +} From e66a9c7125a2129a9a1928909fdf390f813043d0 Mon Sep 17 00:00:00 2001 From: Adam Dunkels Date: Thu, 29 Aug 2013 19:49:12 +0200 Subject: [PATCH 02/14] Emulation support for the TI CC1120 sub-GHz transceiver --- se/sics/mspsim/chip/CC1120.java | 939 ++++++++++++++++++++++++++++++++ 1 file changed, 939 insertions(+) create mode 100755 se/sics/mspsim/chip/CC1120.java diff --git a/se/sics/mspsim/chip/CC1120.java b/se/sics/mspsim/chip/CC1120.java new file mode 100755 index 0000000..54d2774 --- /dev/null +++ b/se/sics/mspsim/chip/CC1120.java @@ -0,0 +1,939 @@ +/* + * 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.chip; + +import java.util.ArrayList; +import java.util.List; + +import se.sics.mspsim.core.IOPort; +import se.sics.mspsim.core.MSP430Core; +import se.sics.mspsim.core.TimeEvent; +import se.sics.mspsim.core.USARTListener; +import se.sics.mspsim.core.USARTSource; + +public class CC1120 extends Radio802154 implements USARTListener { + + /* cc1120-const.h: Configuration registers */ + public final static int CC1120_IOCFG3 = 0x00; + public final static int CC1120_IOCFG2 = 0x01; + public final static int CC1120_IOCFG1 = 0x02; + public final static int CC1120_IOCFG0 = 0x03; + public final static int CC1120_SYNC3 = 0x04; + public final static int CC1120_SYNC2 = 0x05; + public final static int CC1120_SYNC1 = 0x06; + public final static int CC1120_SYNC0 = 0x07; + public final static int CC1120_SYNC_CFG1 = 0x08; + public final static int CC1120_SYNC_CFG0 = 0x09; + public final static int CC1120_DEVIATION_M = 0x0A; + public final static int CC1120_MODCFG_DEV_E = 0x0B; + public final static int CC1120_DCFILT_CFG = 0x0C; + public final static int CC1120_PREAMBLE_CFG1 = 0x0D; + public final static int CC1120_PREAMBLE_CFG0 = 0x0E; + public final static int CC1120_FREQ_IF_CFG = 0x0F; + public final static int CC1120_IQIC = 0x10; + public final static int CC1120_CHAN_BW = 0x11; + public final static int CC1120_MDMCFG1 = 0x12; + public final static int CC1120_MDMCFG0 = 0x13; + public final static int CC1120_DRATE2 = 0x14; + public final static int CC1120_DRATE1 = 0x15; + public final static int CC1120_DRATE0 = 0x16; + public final static int CC1120_AGC_REF = 0x17; + public final static int CC1120_AGC_CS_THR = 0x18; + public final static int CC1120_AGC_GAIN_ADJUST = 0x19; + public final static int CC1120_AGC_CFG3 = 0x1A; + public final static int CC1120_AGC_CFG2 = 0x1B; + public final static int CC1120_AGC_CFG1 = 0x1C; + public final static int CC1120_AGC_CFG0 = 0x1D; + public final static int CC1120_FIFO_CFG = 0x1E; + public final static int CC1120_DEV_ADDR = 0x1F; + public final static int CC1120_SETTLING_CFG = 0x20; + public final static int CC1120_FS_CFG = 0x21; + public final static int CC1120_WOR_CFG1 = 0x22; + public final static int CC1120_WOR_CFG0 = 0x23; + public final static int CC1120_WOR_EVENT0_MSB = 0x24; + public final static int CC1120_WOR_EVENT0_LSB = 0x25; + public final static int CC1120_PKT_CFG2 = 0x26; + public final static int CC1120_PKT_CFG1 = 0x27; + public final static int CC1120_PKT_CFG0 = 0x28; + public final static int CC1120_RFEND_CFG1 = 0x29; + public final static int CC1120_RFEND_CFG0 = 0x2A; + public final static int CC1120_PA_CFG2 = 0x2B; + public final static int CC1120_PA_CFG1 = 0x2C; + public final static int CC1120_PA_CFG0 = 0x2D; + public final static int CC1120_PKT_LEN = 0x2E; + public final static int CC1120_EXTENDED_MEMORY_ACCESS = 0x2F; + + /* cc1120-const.h: Extended register space */ + public final static int CC1120_IF_MIX_CFG = 0x00; + public final static int CC1120_FREQOFF_CFG = 0x01; + public final static int CC1120_TOC_CFG = 0x02; + public final static int CC1120_MARC_SPARE = 0x03; + public final static int CC1120_ECG_CFG = 0x04; + public final static int CC1120_SOFT_TX_DATA_CFG = 0x05; + public final static int CC1120_EXT_CTRL = 0x06; + public final static int CC1120_RCCAL_FINE = 0x07; + public final static int CC1120_RCCAL_COARSE = 0x08; + public final static int CC1120_RCCAL_OFFSET = 0x09; + public final static int CC1120_FREQOFF1 = 0x0A; + public final static int CC1120_FREQOFF0 = 0x0B; + public final static int CC1120_FREQ2 = 0x0C; + public final static int CC1120_FREQ1 = 0x0D; + public final static int CC1120_FREQ0 = 0x0E; + public final static int CC1120_IF_ADC2 = 0x0F; + public final static int CC1120_IF_ADC1 = 0x10; + public final static int CC1120_IF_ADC0 = 0x11; + public final static int CC1120_FS_DIG1 = 0x12; + public final static int CC1120_FS_DIG0 = 0x13; + public final static int CC1120_FS_CAL3 = 0x14; + public final static int CC1120_FS_CAL2 = 0x15; + public final static int CC1120_FS_CAL1 = 0x16; + public final static int CC1120_FS_CAL0 = 0x17; + public final static int CC1120_FS_CHP = 0x18; + public final static int CC1120_FS_DIVTWO = 0x19; + public final static int CC1120_FS_DSM1 = 0x1A; + public final static int CC1120_FS_DSM0 = 0x1B; + public final static int CC1120_FS_DVC1 = 0x1C; + public final static int CC1120_FS_DVC0 = 0x1D; + public final static int CC1120_FS_LBI = 0x1E; + public final static int CC1120_FS_PFD = 0x1F; + public final static int CC1120_FS_PRE = 0x20; + public final static int CC1120_FS_REG_DIV_CML = 0x21; + public final static int CC1120_FS_SPARE = 0x22; + public final static int CC1120_FS_VCO4 = 0x23; + public final static int CC1120_FS_VCO3 = 0x24; + public final static int CC1120_FS_VCO2 = 0x25; + public final static int CC1120_FS_VCO1 = 0x26; + public final static int CC1120_FS_VCO0 = 0x27; + public final static int CC1120_GBIAS6 = 0x28; + public final static int CC1120_GBIAS5 = 0x29; + public final static int CC1120_GBIAS4 = 0x2A; + public final static int CC1120_GBIAS3 = 0x2B; + public final static int CC1120_GBIAS2 = 0x2C; + public final static int CC1120_GBIAS1 = 0x2D; + public final static int CC1120_GBIAS0 = 0x2E; + public final static int CC1120_IFAMP = 0x2F; + public final static int CC1120_LNA = 0x30; + public final static int CC1120_RXMIX = 0x31; + public final static int CC1120_XOSC5 = 0x32; + public final static int CC1120_XOSC4 = 0x33; + public final static int CC1120_XOSC3 = 0x34; + public final static int CC1120_XOSC2 = 0x35; + public final static int CC1120_XOSC1 = 0x36; + public final static int CC1120_XOSC0 = 0x37; + public final static int CC1120_ANALOG_SPARE = 0x38; + public final static int CC1120_PA_CFG3 = 0x39; + public final static int CC1120_WOR_TIME1 = 0x64; + public final static int CC1120_WOR_TIME0 = 0x65; + public final static int CC1120_WOR_CAPTURE1 = 0x66; + public final static int CC1120_WOR_CAPTURE0 = 0x67; + public final static int CC1120_BIST = 0x68; + public final static int CC1120_DCFILTOFFSET_I1 = 0x69; + public final static int CC1120_DCFILTOFFSET_I0 = 0x6A; + public final static int CC1120_DCFILTOFFSET_Q1 = 0x6B; + public final static int CC1120_DCFILTOFFSET_Q0 = 0x6C; + public final static int CC1120_IQIE_I1 = 0x6D; + public final static int CC1120_IQIE_I0 = 0x6E; + public final static int CC1120_IQIE_Q1 = 0x6F; + public final static int CC1120_IQIE_Q0 = 0x70; + public final static int CC1120_RSSI1 = 0x71; + public final static int CC1120_RSSI0 = 0x72; + public final static int CC1120_MARCSTATE = 0x73; + public final static int CC1120_LQI_VAL = 0x74; + public final static int CC1120_PQT_SYNC_ERR = 0x75; + public final static int CC1120_DEM_STATUS = 0x76; + public final static int CC1120_FREQOFF_EST1 = 0x77; + public final static int CC1120_FREQOFF_EST0 = 0x78; + public final static int CC1120_AGC_GAIN3 = 0x79; + public final static int CC1120_AGC_GAIN2 = 0x7A; + public final static int CC1120_AGC_GAIN1 = 0x7B; + public final static int CC1120_AGC_GAIN0 = 0x7C; + public final static int CC1120_SOFT_RX_DATA_OUT = 0x7D; + public final static int CC1120_SOFT_TX_DATA_IN = 0x7E; + public final static int CC1120_ASK_SOFT_RX_DATA = 0x7F; + public final static int CC1120_RNDGEN = 0x80; + public final static int CC1120_MAGN2 = 0x81; + public final static int CC1120_MAGN1 = 0x82; + public final static int CC1120_MAGN0 = 0x83; + public final static int CC1120_ANG1 = 0x84; + public final static int CC1120_ANG0 = 0x85; + public final static int CC1120_CHFILT_I2 = 0x86; + public final static int CC1120_CHFILT_I1 = 0x87; + public final static int CC1120_CHFILT_I0 = 0x88; + public final static int CC1120_CHFILT_Q2 = 0x89; + public final static int CC1120_CHFILT_Q1 = 0x8A; + public final static int CC1120_CHFILT_Q0 = 0x8B; + public final static int CC1120_GPIO_STATUS = 0x8C; + public final static int CC1120_FSCAL_CTRL = 0x8D; + public final static int CC1120_PHASE_ADJUST = 0x8E; + public final static int CC1120_PARTNUMBER = 0x8F; + public final static int CC1120_PARTVERSION = 0x90; + public final static int CC1120_SERIAL_STATUS = 0x91; + public final static int CC1120_RX_STATUS = 0x92; + public final static int CC1120_TX_STATUS = 0x93; + public final static int CC1120_MARC_STATUS1 = 0x94; + public final static int CC1120_MARC_STATUS0 = 0x95; + public final static int CC1120_PA_IFAMP_TEST = 0x96; + public final static int CC1120_FSRF_TEST = 0x97; + public final static int CC1120_PRE_TEST = 0x98; + public final static int CC1120_PRE_OVR = 0x99; + public final static int CC1120_ADC_TEST = 0x9A; + public final static int CC1120_DVC_TEST = 0x9B; + public final static int CC1120_ATEST = 0x9C; + public final static int CC1120_ATEST_LVDS = 0x9D; + public final static int CC1120_ATEST_MODE = 0x9E; + public final static int CC1120_XOSC_TEST1 = 0x9F; + public final static int CC1120_XOSC_TEST0 = 0xA0; + public final static int CC1120_RXFIRST = 0xD2; + public final static int CC1120_TXFIRST = 0xD3; + public final static int CC1120_RXLAST = 0xD4; + public final static int CC1120_TXLAST = 0xD5; + public final static int CC1120_NUM_TXBYTES = 0xD6; + public final static int CC1120_NUM_RXBYTES = 0xD7; + public final static int CC1120_FIFO_NUM_TXBYTES = 0xD8; + public final static int CC1120_FIFO_NUM_RXBYTES = 0xD9; + + public static final int CC1120_SRES = 0x30; // Reset chip.; + public static final int CC1120_SFSTXON = 0x31; // Enable and calibrate + // frequency synthesizer (if + // MCSM0.FS_AUTOCAL=1).; + public static final int CC1120_SXOFF = 0x32; // Turn off crystal + // oscillator.; + public static final int CC1120_SCAL = 0x33; // Calibrate frequency + // synthesizer and turn it off; + public static final int CC1120_SRX = 0x34; // Enable RX. Perform calibration + // first if coming from IDLE + // and; + public static final int CC1120_STX = 0x35; // In IDLE state: Enable TX. + // Perform calibration first if; + public static final int CC1120_SIDLE = 0x36; // Exit RX / TX, turn off + // frequency synthesizer and + // exit; + public static final int CC1120_SAFC = 0x37; // Perform AFC adjustment of the + // frequency synthesizer; + public static final int CC1120_SWOR = 0x38; // Start automatic RX polling + // sequence (Wake-on-Radio); + public static final int CC1120_SPWD = 0x39; // Enter power down mode when + // CSn goes high.; + public static final int CC1120_SFRX = 0x3A; // Flush the RX FIFO buffer.; + public static final int CC1120_SFTX = 0x3B; // Flush the TX FIFO buffer.; + public static final int CC1120_SWORRST = 0x3C; // Reset real time clock.; + public static final int CC1120_SNOP = 0x3D; // No operation. May be used to + // pad strobe commands to two; + + public final static int CC1120_TXFIFO = 0x3F; + public final static int CC1120_RXFIFO = 0x3F; + + public final static int CCA_THRESHOLD = -95; + + public static enum CC1120RadioState { + CC1120_STATE_SLEEP(0b00000)/* 0 */, + CC1120_STATE_IDLE(0b00001), + CC1120_STATE_XOFF(0b00010), + CC1120_STATE_BIAS_SETTLE_MC(0b00011), + CC1120_STATE_REG_SETTLE_MC(0b00100), + CC1120_STATE_MANCAL(0b00101), + CC1120_STATE_BIAS_SETTLE(0b00110), + CC1120_STATE_REG_SETTLE(0b00111), + CC1120_STATE_STARTCAL(0b01000), + CC1120_STATE_BWBOOST(0b01001), + CC1120_STATE_FS_LOCK(0b01010), + CC1120_STATE_IFADCON(0b01011), + CC1120_STATE_ENDCAL(0b01100), + CC1120_STATE_RX(0b01101) /* 13 */, + CC1120_STATE_RX_END(0b01110), + CC1120_STATE_Reserved(0b01111), + CC1120_STATE_TXRX_SWITCH(0b10000), + CC1120_STATE_RX_FIFO_ERR(0b10001), + CC1120_STATE_FSTXON(0b10010), + CC1120_STATE_TX(0b10011), + CC1120_STATE_TX_END(0b10100), + CC1120_STATE_RXTX_SWITCH(0b10101), + CC1120_STATE_TX_FIFO_ERR(0b10110), + CC1120_STATE_IFADCON_TXRX(0b10111); + + private final int state; + + CC1120RadioState(int stateNo) { + state = stateNo; + } + + public int getStateAsInt() { + return state | (0b10 << 5); /* TODO 2 pin state not implemented */ + } + }; + + private TimeEvent sendEvent = new TimeEvent(0, "CC1120 Send") { + public void execute(long t) { + txNext(); + } + }; + + protected boolean DEBUG = false; + + public final static double SYMBOL_PERIOD = 0.016; /* TODO XXX 16 us */ + public final static double FREQUENCY_CHANNEL_0 = 902; /* MHz */ + public final static double FREQUENCY_CHANNEL_WIDTH = 0.125; /* MHz */ + + private StateListener stateListener = null; + private ReceiverListener receiverListener = null; + + /* RSSI1: RSSI_11_4 */ + private int currentRssiReg1 = 0; + /* RSSI0: RSSI_3_0 TODO XXX Ignored */ + private int currentRssiReg0 = 0; + + private double frequency = -1; + private int nextFreq0 = -1, nextFreq1 = -1, nextFreq2 = -1; /* regs */ + private boolean changeFrequencyNextState; + + private CC1120RadioState state = null; + + protected List txfifo = new ArrayList(); + protected List rxfifo = new ArrayList(); + + protected int[] registers = new int[64]; + protected int[] extendedRegisters = new int[256]; + protected int[] memory = new int[512]; + + private boolean chipSelect; + + private IOPort gdo0Port = null; + private int gdo0Pin = -1; + private IOPort gdo2Port = null; + private int gdo2Pin = -1; + + public CC1120(MSP430Core cpu) { + super("CC1120", "Radio", cpu); + reset(); + } + + void strobe(int cmd) { + switch (cmd) { + case CC1120_SRES: + System.out.println("CC1120_SRES not implemented"); + reset(); + break; + + case CC1120_SFSTXON: + System.out.println("CC1120_SFSTXON not implemented"); + break; + + case CC1120_SXOFF: + System.out.println("CC1120_SXOFF not implemented"); + break; + + case CC1120_SCAL: + changeFrequencyNextState = true; + setState(CC1120RadioState.CC1120_STATE_IDLE); + break; + + case CC1120_SRX: + setState(CC1120RadioState.CC1120_STATE_RX); + break; + + case CC1120_STX: + int len = (int) (0xff&txfifo.get(0)); + txFooterCountdown = 1 + len + 1/*len*/; + if (DEBUG) { + System.out.println("TX started: len = " + len + ", txFooterCountdown = " + txFooterCountdown); + } + txNext(); + setState(CC1120RadioState.CC1120_STATE_TX); + break; + + case CC1120_SIDLE: + setState(CC1120RadioState.CC1120_STATE_IDLE); + break; + + case CC1120_SAFC: + System.out.println("CC1120_SAFC not implemented"); + break; + + case CC1120_SWOR: + System.out.println("CC1120_SWOR not implemented"); + break; + + case CC1120_SPWD: + System.out.println("CC1120_SPWD almost implemented"); + /* TODO XXX + * Wait until CS is de-asserted. (We should at least wait until radio is done + * receiving or transmitting.)*/ + setState(CC1120RadioState.CC1120_STATE_SLEEP); + break; + + case CC1120_SFRX: + rxfifo.clear(); + rxExpectedLen = -1; + rxGotSynchByte = false; + setGDO0(false); + /* printRXFIFO(); */ + break; + + case CC1120_SFTX: + txfifo.clear(); + txFooterCountdown = -1; + /* printTXFIFO(); */ + break; + + case CC1120_SWORRST: + System.out.println("CC1120_SWORRST not implemented"); + break; + + case CC1120_SNOP: + System.out.println("CC1120_SNOP not implemented"); + break; + + default: + System.out.printf("strobe(0x%02x)\n", cmd); + break; + } + } + + public int getLQI() { + return 0; /* TODO */ + }; + + public void setLQI(int lqi) { + } + + /* SPI */ + private final static int SPI_READ_BIT = 0x80; + private final static int SPI_BURST_BIT = 0x40; + private final static int SPI_EXTENDED_ADDRESS = 0x2F; + + private boolean spiAwaitingAddressExtended = false; + private boolean spiExtendedMode = false; + private boolean spiBurstMode = false; + private boolean spiReadMode = false; + private boolean spiGotAddress = false; + private int spiAddress; + + private static boolean spiIsBurst(int data) { + return (data & SPI_BURST_BIT) == SPI_BURST_BIT; + } + + private static boolean spiIsRead(int data) { + return (data & SPI_READ_BIT) == SPI_READ_BIT; + } + + private static boolean spiIsExtended(int data) { + data &= ~SPI_BURST_BIT; + data &= ~SPI_READ_BIT; + return data == SPI_EXTENDED_ADDRESS; + } + + private static boolean spiIsStrobe(int data) { + data = data & 0x3f; + return data >= 0x30 && data <= 0x3e; + } + + private void spiResetState() { + spiAwaitingAddressExtended = false; + spiBurstMode = false; + spiReadMode = false; + spiGotAddress = false; + spiExtendedMode = false; + spiAddress = 0xFF; + } + + public void dataReceived(USARTSource source, int data) { + if (spiGotAddress) { + if (!spiBurstMode) { + /* Single access mode */ + if (spiReadMode) { + source.byteReceived(getReg(spiAddress, spiExtendedMode)); + } else { + source.byteReceived(setReg(spiAddress, data, + spiExtendedMode)); + } + spiResetState(); + } else { + /* Burst mode */ + if (spiReadMode) { + source.byteReceived(getReg(spiAddress, spiExtendedMode)); + } else { + source.byteReceived(setReg(spiAddress, data, + spiExtendedMode)); + } + + if (spiAddress < CC1120_TXFIFO) { + spiAddress++; + } + } + return; + } + + if (spiAwaitingAddressExtended) { + spiAddress = data; + spiGotAddress = true; + spiAwaitingAddressExtended = false; + spiExtendedMode = true; + + /* Return MARCSTATE */ + source.byteReceived(getMarcstate()); + return; + } + + /* Read/write. Burst/single. Extended/normal. */ + spiBurstMode = spiIsBurst(data); + spiReadMode = spiIsRead(data); + spiExtendedMode = spiIsExtended(data); + spiAwaitingAddressExtended = spiIsExtended(data); + + /* Is this a strobe command */ + if (!spiBurstMode && !spiAwaitingAddressExtended && spiIsStrobe(data)) { + /* Strobe command */ + strobe(data); + source.byteReceived(0); + spiResetState(); + return; + } + + if (!spiAwaitingAddressExtended) { + spiAddress = data & 0x3f; + spiGotAddress = true; + } + + /* Return MARCSTATE */ + source.byteReceived(getMarcstate()); + return; + } + int setReg(int address, int data, boolean extended) { + switch (address) { + case CC1120_TXFIFO: + txfifo.add((byte) data); + /* printTXFIFO(); */ + return txfifo.size(); + case CC1120_FREQ2: /* XXX This is probably wrong, extended right? */ + nextFreq2 = data; + return 0; + case CC1120_FREQ1: + nextFreq1 = data; + return 0; + case CC1120_FREQ0: + nextFreq0 = data; + return 0; + } + + System.out.println(String.format("setReg(0x%02x, %s) 0x%02x", address, + extended ? "extended" : "normal", data)); + if (extended) { + int oldValue = extendedRegisters[address]; + extendedRegisters[address] = data; + configurationChanged(address, oldValue, data); + return oldValue; + } + int oldValue = registers[address]; + registers[address] = data; + configurationChanged(address, oldValue, data); + return oldValue; + } + int getReg(int address, boolean extended) { + /* MSP430Core.profiler.printStackTrace(System.out); */ + if (extended) { + switch (address) { + case CC1120_MARCSTATE: + return getMarcstate(); + case CC1120_NUM_RXBYTES: + return rxfifo.size(); + case CC1120_NUM_TXBYTES: + return txfifo.size(); + case CC1120_RSSI1: + return currentRssiReg1; + case CC1120_RSSI0: + int ret = 0; + ret += (0b1111 & currentRssiReg0); /* RSSI_3_0 */ + + ret = ret << 1; + if(getRSSI() < CCA_THRESHOLD) { + ret += 1; /* Carrier detected */ + } else { + ret += 0; /* No carrier detected */ + } + + ret = ret << 1; + ret += 1; /* TODO XXX Carrier sense is always valid */ + + ret = ret << 1; + ret += 1; /* TODO XXX RSSI is always valid */ + + return ret; + } + } + + switch (address) { + case CC1120_RXFIFO: + if (rxfifo.size() > 0) { + int ret = (int) rxfifo.remove(0); + /* printRXFIFO(); */ + return ret; + } + System.err.println("Warning: reading from empty RXFIFO"); + return -1; + } + + if (address != CC1120.CC1120_MARCSTATE) { + System.out + .println(String.format("getReg(0x%02x, %s) 0x%02x", + address, extended ? "extended" : "normal", + extended ? extendedRegisters[address] + : registers[address])); + } + + if (extended) { + return extendedRegisters[address]; + } + return registers[address]; + } + + public boolean isReadyToReceive() { + /* TODO Implement me */ + if (getState() == CC1120RadioState.CC1120_STATE_IDLE) { + return false; + } + if (getState() == CC1120RadioState.CC1120_STATE_SLEEP) { + return false; + } + return true; + } + + + /* txFooterCountdown: send CRC footer in these many bytes */ + protected int txFooterCountdown = -1; + + /* TX/RX states */ + public static final int NUM_PREAMBLE = 4; + public static final int NUM_SYNCH = 4; + public static final byte SYNCH_BYTE_LAST = (byte) 0xDE; + private boolean txSentSynchByte = false; + private int txSendSynchByteCnt = 0; + private boolean txSentFirstCRC = false; + void txNext() { + if (txFooterCountdown < 0) { + System.out.println("Warning: Aborting transmit since txFooterCountdown=" + txFooterCountdown); + return; + } + + /* Send preamble and synch bytes */ + if (!txSentSynchByte) { + /* Send NUM_PREAMBLE preamble bytes */ + if (txSendSynchByteCnt < NUM_PREAMBLE) { + txSendSynchByteCnt++; + if (rfListener != null) { + rfListener.receivedByte((byte) (0xaa)); + } + cpu.scheduleTimeEventMillis(sendEvent, SYMBOL_PERIOD * 2); + return; + } + /* Send NUM_SYNCH-1 synch bytes */ + if (txSendSynchByteCnt < NUM_PREAMBLE + NUM_SYNCH - 1) { + txSendSynchByteCnt++; + if (rfListener != null) { + rfListener.receivedByte((byte) (SYNCH_BYTE_LAST + 1)); + } + cpu.scheduleTimeEventMillis(sendEvent, SYMBOL_PERIOD * 2); + return; + } + /* Send last synch byte */ + if (txSendSynchByteCnt < NUM_PREAMBLE + NUM_SYNCH) { + txSendSynchByteCnt++; + if (rfListener != null) { + rfListener.receivedByte((byte) (SYNCH_BYTE_LAST)); + } + cpu.scheduleTimeEventMillis(sendEvent, SYMBOL_PERIOD * 2); + + txSentSynchByte = true; + return; + } + + txSentFirstCRC = false; + } + + if (txSentFirstCRC) { + /* send second CRC byte */ + if (rfListener != null) { + rfListener.receivedByte((byte) (0xef)); + } + if (!txfifo.isEmpty()) { + System.out.println("Warning: TXFIFO not empty after sending CRC bytes"); + } + setStateRX(); + txSentSynchByte = false; + txSendSynchByteCnt = 0; + txSentFirstCRC = false; + return; + } + + txFooterCountdown--; + + if (txFooterCountdown == 0) { + /* countdown is zero, send first CRC byte */ + if (rfListener != null) { + rfListener.receivedByte((byte) (0xee)); + } + txSentFirstCRC = true; + cpu.scheduleTimeEventMillis(sendEvent, SYMBOL_PERIOD * 2); + return; + } + + /* Send payload byte (including first FIFO length byte) */ + if (txfifo.isEmpty()) { + System.out.println("Warning: TXFIFO is empty, sending zero-byte, txFooterCountdown=" + txFooterCountdown); + if (rfListener != null) { + rfListener.receivedByte((byte) 0); + } + cpu.scheduleTimeEventMillis(sendEvent, SYMBOL_PERIOD * 2); + return; + } + + if (rfListener != null) { + rfListener.receivedByte((byte) (txfifo.get(0).intValue())); + } + txfifo.remove(0); + cpu.scheduleTimeEventMillis(sendEvent, SYMBOL_PERIOD * 2); + + /* printTXFIFO(); */ + } + + public void setGDO0(IOPort port, int pin) { + gdo0Port = port; + gdo0Pin = pin; + } + public void setGDO0(boolean active) { + /* TODO Read active low/high configuration */ + + if (active) { + gdo0Port.setPinState(gdo0Pin, IOPort.PinState.HI); + } else { + gdo0Port.setPinState(gdo0Pin, IOPort.PinState.LOW); + } + } + + public void setGDO2(IOPort port, int pin) { + gdo2Port = port; + gdo2Pin = pin; + } + public void setGDO2(boolean active) { + gdo2Port.setPinState(gdo2Pin, active ? IOPort.PinState.LOW + : IOPort.PinState.HI); + } + + private void printRXFIFO() { + System.out.print(String.format("RXFIFO[%03d]: ", rxfifo.size())); + for (int i = 0; i < rxfifo.size(); i++) { + System.out.print(String.format("%02x", rxfifo.get(i))); + } + System.out.println(); + } + private void printTXFIFO() { + System.out.print(String.format("TXFIFO[%03d]: ", txfifo.size())); + for (int i = 0; i < txfifo.size(); i++) { + System.out.print(String.format("%02x", txfifo.get(i))); + } + System.out.println(); + } + + public CC1120RadioState getState() { + return state; + } + public interface StateListener { + public void newState(CC1120RadioState state); + } + public void setStateListener(StateListener listener) { + stateListener = listener; + } + public interface ReceiverListener { + public void newState(boolean on); + } + public void setReceiverListener(ReceiverListener listener) { + receiverListener = listener; + } + + boolean receiverOn = false; + boolean setState(CC1120RadioState newState) { + if (newState != CC1120RadioState.CC1120_STATE_IDLE + && newState != CC1120RadioState.CC1120_STATE_RX + && newState != CC1120RadioState.CC1120_STATE_TX) { + System.out.println("setState(" + newState + ")"); + } + + if (changeFrequencyNextState + && newState == CC1120RadioState.CC1120_STATE_RX + || newState == CC1120RadioState.CC1120_STATE_TX) { + changeFrequencyNextState = false; + frequency = ((0xff & nextFreq0) << 0) + ((0xff & nextFreq1) << 8) + + ((0xff & nextFreq2) << 16); + + frequency *= 32; /* frequency oscillator */ + frequency /= 65536; + frequency /= 4; /* LO divider */ + + /* System.out.println("Activating new frequency: " + frequency + " MHz"); */ + if (channelListener != null) { + channelListener.channelChanged((int) Math + .round((frequency - FREQUENCY_CHANNEL_0) + / FREQUENCY_CHANNEL_WIDTH)); + } + } + + state = newState; + + /* Notify state listener */ + if (stateListener != null) { + stateListener.newState(state); + } + if (receiverListener != null && isReadyToReceive() != receiverOn) { + receiverOn = isReadyToReceive(); + receiverListener.newState(receiverOn); + } + return true; + } + + boolean rxGotSynchByte = false; + private int rxExpectedLen = -1; + public void receivedByte(byte data) { + if (!rxGotSynchByte) { + /* Await synch byte */ + if (data == SYNCH_BYTE_LAST) { + rxGotSynchByte = true; + setGDO0(true); + } + return; + } + + if (rxExpectedLen < 0) { + rxExpectedLen = 1/*len*/ + (int) data/*payload*/ + 2/*CRC*/; + } + + rxExpectedLen--; + if (rxExpectedLen == 0) { + setGDO0(false); + rxExpectedLen = -1; + rxGotSynchByte = false; + } + + rxfifo.add(data); + /* printRXFIFO(); */ + } + + public String info() { + return "CC1120 info: [not implemented]"; + } + + public int getRegister(int register) { + return registers[register]; + } + + public void setRegister(int register, int data) { + registers[register] = data; + } + + public void setRSSI(int power) { + currentRssiReg1 = 0xff&power; + } + public int getRSSI() { + return currentRssiReg1; + } + + public int getActiveFrequency() { + return (int) (1000 * Math.round(frequency)); /* kHz */ + } + + public int getActiveChannel() { + return (int) Math.round((frequency - FREQUENCY_CHANNEL_0) + / FREQUENCY_CHANNEL_WIDTH); + } + + public int getOutputPowerIndicator() { + return 1; + } + + public double getFrequency() { + return frequency; + } + + public void notifyReset() { + super.notifyReset(); + setChipSelect(false); + } + + public int getOutputPower() { + return 1; + } + + public int getOutputPowerMax() { + return 1; + } + + public int getOutputPowerIndicatorMax() { + return 1; + } + + /***************************************************************************** + * Chip APIs + *****************************************************************************/ + + public int getModeMax() { + return 0; + } + + /* return data in register at the correct position */ + public int getConfiguration(int parameter) { + return registers[parameter]; + } + + public boolean getChipSelect() { + return chipSelect; + } + + public void setChipSelect(boolean select) { + chipSelect = select; + if (!chipSelect) { + spiResetState(); + } + + if (DEBUG) { + log("setting chipSelect: " + chipSelect); + } + } + + void setStateRX() { + setState(CC1120RadioState.CC1120_STATE_RX); + } + + void reset() { + setState(CC1120RadioState.CC1120_STATE_IDLE); + } + + int getMarcstate() { + return getState().getStateAsInt(); + } + +} From 5d60f2736cddd310894b49e05fdcc917b92bcd7b Mon Sep 17 00:00:00 2001 From: Adam Dunkels Date: Thu, 29 Aug 2013 19:49:37 +0200 Subject: [PATCH 03/14] Emulation support for the Microchip ENC28J60 Ethernet transceiver --- se/sics/mspsim/chip/Enc28J60.java | 272 ++++++++++++++++++++++++++++++ 1 file changed, 272 insertions(+) create mode 100755 se/sics/mspsim/chip/Enc28J60.java diff --git a/se/sics/mspsim/chip/Enc28J60.java b/se/sics/mspsim/chip/Enc28J60.java new file mode 100755 index 0000000..295be4f --- /dev/null +++ b/se/sics/mspsim/chip/Enc28J60.java @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2013, 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. + */ + +/* This is a stub implementation for the Enc28j60 Ethernet chip. This ignores + * the majority of configuration and setup, and only provides the very minimum + * functionality to support a simple bit-banged Thingsquare Mist driver. */ + +package se.sics.mspsim.chip; + +import java.util.ArrayList; + +import se.sics.mspsim.core.Chip; +import se.sics.mspsim.core.IOPort; +import se.sics.mspsim.core.IOPort.PinState; +import se.sics.mspsim.core.MSP430Core; + +public class Enc28J60 extends Chip { + protected boolean DEBUG = false; + + public static final int EIE = 0x1b; + public static final int EIR = 0x1c; + public static final int ESTAT = 0x1d; + public static final int ECON2 = 0x1e; + public static final int ECON1 = 0x1f; + + public static final int ESTAT_CLKRDY = 0x01; + public static final int ESTAT_TXABRT = 0x02; + + public static final int ECON1_RXEN = 0x04; + public static final int ECON1_TXRTS = 0x08; + + public static final int ECON2_AUTOINC = 0x80; + public static final int ECON2_PKTDEC = 0x40; + + public static final int EPKTCNT = 0x19; + + private static final int WBM_COMMAND = 0x3a; /* Note: 0x7a with write bit set */ + private static final int RBM_COMMAND = 0x3a; /* Note: 0x7a with write bit set */ + + private IOPort myPort; + + private int myClk; + private int myMosi; + private int myChipSelect; + private int myMisoBit; + + public Enc28J60(MSP430Core cpu, IOPort port, int clk, int mosi, + int miso, int chipSelect) { + super("Enc28J60", "Ethernet", cpu); + + myPort = port; + myClk = (1 << clk); + myMosi = (1 << mosi); + myChipSelect = (1 << chipSelect); + + myMisoBit = miso; + } + + public void log(String msg) { + if (DEBUG) { + System.out.println(msg); + } + } + + private boolean writingToWBM = false; + private boolean readingFromRBM = false; + private boolean nextEcon1 = false; + private boolean nextEcon2 = false; + + private ArrayList wbmData = new ArrayList(); + + private ArrayList rbmPackets = new ArrayList(); + private static class RbmPacket { + ArrayList data = new ArrayList(); + boolean wasRead = false; + } + + public void writePacket(byte[] data) { + RbmPacket p = new RbmPacket(); + int len = data.length; + + p.data.add(new Byte((byte) 0x00)); /* ignored: next packet pointer */ + p.data.add(new Byte((byte) 0x00)); /* ignored: next packet pointer */ + + p.data.add(new Byte((byte) (len & 0xff))); /* length */ + p.data.add(new Byte((byte) ((len >> 8) & 0xff))); /* length */ + + p.data.add(new Byte((byte) 0x00)); /* ignored: status */ + p.data.add(new Byte((byte) 0x00)); /* ignored: status */ + + for (byte b : data) { + p.data.add(new Byte(b)); /* data */ + } + + rbmPackets.add(p); + log("Enc28j60: nr pending packets increased to: " + rbmPackets.size()); + } + + private PacketListener listener = null; + public static interface PacketListener { + public void packetSent(Byte[] packetData); + } + public void setPacketListener(PacketListener l) { + listener = l; + } + + private int inputByte(int data) { + int val = 0x00; + + if (writingToWBM) { + wbmData.add(new Byte((byte) data)); + val = 0x00; + return val; + } else if (readingFromRBM) { + if (rbmPackets.size() > 0) { + if (rbmPackets.get(0).data.isEmpty()) { + log("Enc28j60: warning, packet data is already consumed, returning 0"); + } else { + val = rbmPackets.get(0).data.remove(0); + rbmPackets.get(0).wasRead = true; + } + } else { + log("Enc28j60: warning, no packet in rbm, returning 0"); + val = 0x00; + } + return val; + } + + + /* Strip optional write flag */ + boolean writing = (data & 0x40) != 0; + data = (data & ~0x40); + + if (nextEcon1) { + if ((data & ECON1_TXRTS) != 0) { + log("Transmitting enc28j60 packet, size: " + wbmData.size()); + if (listener != null) { + wbmData.remove(0); + listener.packetSent(wbmData.toArray(new Byte[0])); + } + wbmData.clear(); + } + nextEcon1 = false; + } else if (nextEcon2) { + if (!rbmPackets.isEmpty() && rbmPackets.get(0).wasRead) { + rbmPackets.remove(0); + log("Enc28j60: nr pending packets decreased to: " + rbmPackets.size()); + } + nextEcon2 = false; + } else if (data == ECON1) { + /* we are awaiting a ECON1 command */ + nextEcon1 = true; + } else if (data == ECON2) { + /* we are awaiting a ECON2 command */ + nextEcon2 = true; + } else if (data == EPKTCNT) { + return rbmPackets.size(); + } else if (data == ESTAT) { + /* chip is always ready */ + val = ESTAT_CLKRDY; + } else if (writing && data == WBM_COMMAND) { + writingToWBM = true; + } else if (data == RBM_COMMAND) { + if (rbmPackets.size() > 0) { + if (rbmPackets.get(0).data.isEmpty()) { + log("Enc28j60: warning, packet data is already consumed, returning 0"); + } else { + val = rbmPackets.get(0).data.remove(0); + rbmPackets.get(0).wasRead = true; + } + } else { + log("Enc28j60: warning, no packet in rbm, returning 0"); + val = 0x00; + } + readingFromRBM = true; + } + return val; + } + + + private int spiOut = 0; /* byte being sent over SPI */ + private int spiOutCount = 0; + private int spiIn = 0; /* byte being received over SPI */ + private int spiInCount = 0; + public void write(IOPort port, int data) { + if (port != myPort) { + /* ignore */ + return; + } + + boolean chipSelect = (data & myChipSelect) == 0; + boolean clk = (data & myClk) != 0; + boolean mosi = (data & myMosi) != 0; + + if (!chipSelect) { + if (readingFromRBM && spiOutCount == 0 && !rbmPackets.isEmpty()) { + /* XXX Hack: pushing back last byte to rbm */ + rbmPackets.get(0).data.add(0, (byte)spiOut); + } + writingToWBM = false; + readingFromRBM = false; + } + + if (!chipSelect || !clk) { + /* ignore */ + //log("write ignored: chipSelect " + chipSelect + ", clk " + clk); + return; + } + + /* Prepare next outgoing bit on miso */ + if (spiOutCount < 8) { + spiOutCount++; + + if ((spiOut & 0x80) != 0) { + port.setPinState(myMisoBit, PinState.HI); + } else { + port.setPinState(myMisoBit, PinState.LOW); + } + + spiOut = (spiOut << 1); + } + + /* Read next incoming bit on mosi */ + spiIn = (spiIn << 1); + if (mosi) { + spiIn |= 0x01; + } + spiInCount++; + + if (spiInCount >= 8) { + /* We've now read all 8 bits on mosi */ + spiOut = inputByte(spiIn); + spiOutCount = 0; + spiIn = 0; + spiInCount = 0; + } + } + + public int getConfiguration(int parameter) { + return -1; + } + + public int getModeMax() { + return -1; + } +} From f1e8246a1d623ae342791d42e88c4fde219890f2 Mon Sep 17 00:00:00 2001 From: Adam Dunkels Date: Thu, 29 Aug 2013 19:50:24 +0200 Subject: [PATCH 04/14] Emulation support for the TI CC430 sub-GHz SoC --- images/cc430.jpg | Bin 0 -> 41071 bytes se/sics/mspsim/config/CC430f5137Config.java | 166 +++++++++++++ se/sics/mspsim/core/PMMDummy.java | 56 +++++ se/sics/mspsim/core/RF1A.java | 248 ++++++++++++++++++++ se/sics/mspsim/platform/ti/CC430Node.java | 84 +++++++ 5 files changed, 554 insertions(+) create mode 100755 images/cc430.jpg create mode 100644 se/sics/mspsim/config/CC430f5137Config.java create mode 100755 se/sics/mspsim/core/PMMDummy.java create mode 100644 se/sics/mspsim/core/RF1A.java create mode 100644 se/sics/mspsim/platform/ti/CC430Node.java diff --git a/images/cc430.jpg b/images/cc430.jpg new file mode 100755 index 0000000000000000000000000000000000000000..19d84c9b2f21591f35245523478c609d7e3445d1 GIT binary patch literal 41071 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 literal 0 HcmV?d00001 diff --git a/se/sics/mspsim/config/CC430f5137Config.java b/se/sics/mspsim/config/CC430f5137Config.java new file mode 100644 index 0000000..979c905 --- /dev/null +++ b/se/sics/mspsim/config/CC430f5137Config.java @@ -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 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); + } + +} diff --git a/se/sics/mspsim/core/PMMDummy.java b/se/sics/mspsim/core/PMMDummy.java new file mode 100755 index 0000000..e69bc92 --- /dev/null +++ b/se/sics/mspsim/core/PMMDummy.java @@ -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); + } + } +} diff --git a/se/sics/mspsim/core/RF1A.java b/se/sics/mspsim/core/RF1A.java new file mode 100644 index 0000000..840810d --- /dev/null +++ b/se/sics/mspsim/core/RF1A.java @@ -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); + } + } +} diff --git a/se/sics/mspsim/platform/ti/CC430Node.java b/se/sics/mspsim/platform/ti/CC430Node.java new file mode 100644 index 0000000..05f5266 --- /dev/null +++ b/se/sics/mspsim/platform/ti/CC430Node.java @@ -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); + } +} \ No newline at end of file From 4c11cce00d51cc919d0d3b287de2b9394c096e71 Mon Sep 17 00:00:00 2001 From: Adam Dunkels Date: Thu, 29 Aug 2013 19:51:40 +0200 Subject: [PATCH 05/14] Emulation support for the exp1101 platform: the MSP430f5838 experimenter's board with the CC1101 transceiver --- se/sics/mspsim/platform/ti/Exp1101Node.java | 109 ++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 se/sics/mspsim/platform/ti/Exp1101Node.java diff --git a/se/sics/mspsim/platform/ti/Exp1101Node.java b/se/sics/mspsim/platform/ti/Exp1101Node.java new file mode 100644 index 0000000..e9288a8 --- /dev/null +++ b/se/sics/mspsim/platform/ti/Exp1101Node.java @@ -0,0 +1,109 @@ +package se.sics.mspsim.platform.ti; + +import java.io.IOException; + +import se.sics.mspsim.chip.CC1101; +import se.sics.mspsim.config.MSP430f5437Config; +import se.sics.mspsim.core.EmulationException; +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 Exp1101Node extends GenericNode implements PortListener, USARTListener { + + public static final int CC1101_GDO0 = 7; /* 1.7 */ + public static final int CC1101_GDO2 = 3; /* 1.3 */ + + public static final int CC1101_CHIP_SELECT = (1 << 0); // 3.0 + + IOPort port1; + IOPort port3; + IOPort port4; + IOPort port5; + IOPort port7; + IOPort port8; + + public static final int LEDS_CONF_RED = (1 << 0); // 1.0 + public static final int LEDS_CONF_YELLOW = (1 << 1); // 1.1 + + public CC1101 radio; + + public Exp1101Node() { + super("Exp1101", new MSP430f5437Config()); + } + + public void dataReceived(USARTSource source, int data) { + radio.dataReceived(source, data); + + /* if nothing selected, just write back a random byte to these devs */ + if (!radio.getChipSelect()) { + source.byteReceived(0); + } + } + + public void portWrite(IOPort source, int data) { + if (source == port3) { + // Chip select = active low... + radio.setChipSelect((data & CC1101_CHIP_SELECT) == 0); + } + } + + 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) { + radio = new CC1101(cpu); + radio.setGDO0(port1, CC1101_GDO0); + radio.setGDO2(port1, CC1101_GDO2); + ((USARTSource) usart0).addUSARTListener(this); + } else { + throw new EmulationException("Could not setup exp1101 mote - missing USCI B0"); + } + + IOUnit usart = cpu.getIOUnit("USCI A1"); + 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 A1"); + if (usart instanceof USARTSource) { + SerialMon serial = new SerialMon((USARTSource)usart, "USCI A1 Port Output"); + registry.registerComponent("serialgui", serial); + } + } + } + + public int getModeMax() { + return 0; + } + + public static void main(String[] args) throws IOException { + Exp1101Node node = new Exp1101Node(); + ArgumentManager config = new ArgumentManager(); + config.handleArguments(args); + node.setupArgs(config); + } +} From 39d0c101fa2d506e249b43a98a9665928f0a873a Mon Sep 17 00:00:00 2001 From: Adam Dunkels Date: Thu, 29 Aug 2013 19:51:58 +0200 Subject: [PATCH 06/14] Emulation support for the exp1120 platform: the MSP430f5838 experimenter's board with the CC1120 transceiver --- se/sics/mspsim/platform/ti/Exp1120Node.java | 109 ++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 se/sics/mspsim/platform/ti/Exp1120Node.java diff --git a/se/sics/mspsim/platform/ti/Exp1120Node.java b/se/sics/mspsim/platform/ti/Exp1120Node.java new file mode 100644 index 0000000..00a5944 --- /dev/null +++ b/se/sics/mspsim/platform/ti/Exp1120Node.java @@ -0,0 +1,109 @@ +package se.sics.mspsim.platform.ti; + +import java.io.IOException; + +import se.sics.mspsim.chip.CC1120; +import se.sics.mspsim.config.MSP430f5437Config; +import se.sics.mspsim.core.EmulationException; +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 Exp1120Node extends GenericNode implements PortListener, USARTListener { + + public static final int CC1120_GDO0 = 7; /* 1.7 */ + public static final int CC1120_GDO2 = 3; /* 1.3 */ + + public static final int CC1120_CHIP_SELECT = (1 << 0); // 3.0 + + IOPort port1; + IOPort port3; + IOPort port4; + IOPort port5; + IOPort port7; + IOPort port8; + + public static final int LEDS_CONF_RED = (1 << 0); // 1.0 + public static final int LEDS_CONF_YELLOW = (1 << 1); // 1.1 + + public CC1120 radio; + + public Exp1120Node() { + super("Exp1120", new MSP430f5437Config()); + } + + public void dataReceived(USARTSource source, int data) { + radio.dataReceived(source, data); + + /* if nothing selected, just write back a random byte to these devs */ + if (!radio.getChipSelect()) { + source.byteReceived(0); + } + } + + public void portWrite(IOPort source, int data) { + if (source == port3) { + // Chip select = active low... + radio.setChipSelect((data & CC1120_CHIP_SELECT) == 0); + } + } + + 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) { + radio = new CC1120(cpu); + radio.setGDO0(port1, CC1120_GDO0); + radio.setGDO2(port1, CC1120_GDO2); + ((USARTSource) usart0).addUSARTListener(this); + } else { + throw new EmulationException("Error creating Exp1120Node: no USCI B0"); + } + + IOUnit usart = cpu.getIOUnit("USCI A1"); + 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 A1"); + if (usart instanceof USARTSource) { + SerialMon serial = new SerialMon((USARTSource)usart, "USCI A1 Port Output"); + registry.registerComponent("serialgui", serial); + } + } + } + + public int getModeMax() { + return 0; + } + + public static void main(String[] args) throws IOException { + Exp1120Node node = new Exp1120Node(); + ArgumentManager config = new ArgumentManager(); + config.handleArguments(args); + node.setupArgs(config); + } +} From 7e4430c18d726dea5298e890b32b62783d85fa01 Mon Sep 17 00:00:00 2001 From: Adam Dunkels Date: Thu, 29 Aug 2013 19:52:26 +0200 Subject: [PATCH 07/14] Emulation support for the TI TRXEB board with a CC1120 sub-GHz transceiver --- se/sics/mspsim/platform/ti/Trxeb1120Node.java | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100755 se/sics/mspsim/platform/ti/Trxeb1120Node.java diff --git a/se/sics/mspsim/platform/ti/Trxeb1120Node.java b/se/sics/mspsim/platform/ti/Trxeb1120Node.java new file mode 100755 index 0000000..6b22058 --- /dev/null +++ b/se/sics/mspsim/platform/ti/Trxeb1120Node.java @@ -0,0 +1,105 @@ +package se.sics.mspsim.platform.ti; + +import se.sics.mspsim.chip.CC1120; +import se.sics.mspsim.chip.Enc28J60; +import se.sics.mspsim.config.MSP430f5437Config; +import se.sics.mspsim.core.EmulationException; +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; + +public class Trxeb1120Node extends GenericNode implements PortListener, USARTListener { + + public static final int CC1120_GDO0 = 7; /* 1.7 */ + public static final int CC1120_GDO2 = 3; /* 1.3 */ + public static final int CC1120_CHIP_SELECT = (1 << 0); // 3.0 + + public static final int ENC28J60_CLK = 2; /* 10.2 */ + public static final int ENC28J60_MOSI = 1; /* 10.1 */ + public static final int ENC28J60_MISO = 0; /* 10.0 */ + public static final int ENC28J60_CHIP_SELECT = 3; /* 10.3 */ + + IOPort port1; + IOPort port3; + IOPort port4; + IOPort port5; + IOPort port7; + IOPort port8; + IOPort port10; + + public CC1120 radio; + public Enc28J60 enc = null; + + private boolean withEnc; + + public Trxeb1120Node(boolean withEnc) { + super("Trxeb1120", new MSP430f5437Config()); + + this.withEnc = withEnc; + } + + public void dataReceived(USARTSource source, int data) { + radio.dataReceived(source, data); + + /* if nothing selected, just write back a random byte to these devs */ + if (!radio.getChipSelect()) { + source.byteReceived(0); + } + } + + public void portWrite(IOPort source, int data) { + if (source == port3) { + // Chip select = active low... + radio.setChipSelect((data & CC1120_CHIP_SELECT) == 0); + } else if (withEnc && source == port10) { + enc.write(port10, 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) { + radio = new CC1120(cpu); + radio.setGDO0(port1, CC1120_GDO0); + radio.setGDO2(port1, CC1120_GDO2); + ((USARTSource) usart0).addUSARTListener(this); + } else { + throw new EmulationException("Error creating Trxeb1120Node: no USCI B0"); + } + + if (withEnc) { + port10 = cpu.getIOUnit(IOPort.class, "P10"); + port10.addPortListener(this); + enc = new Enc28J60(cpu, port10, ENC28J60_CLK, ENC28J60_MOSI, ENC28J60_MISO, ENC28J60_CHIP_SELECT); + } + + IOUnit usart = cpu.getIOUnit("USCI A1"); + if (usart instanceof USARTSource) { + registry.registerComponent("serialio", usart); + } + } + + public void setupNode() { + setupNodePorts(); + } + + public int getModeMax() { + return 0; + } +} From e45bb6dbbc1b60f2931d83652718fa5a6f2693d3 Mon Sep 17 00:00:00 2001 From: Adam Dunkels Date: Thu, 29 Aug 2013 19:55:53 +0200 Subject: [PATCH 08/14] Emulation support for the TI TRXEB board with a CC2520 2.4 GHz transceiver --- se/sics/mspsim/platform/ti/Trxeb2520Node.java | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100755 se/sics/mspsim/platform/ti/Trxeb2520Node.java diff --git a/se/sics/mspsim/platform/ti/Trxeb2520Node.java b/se/sics/mspsim/platform/ti/Trxeb2520Node.java new file mode 100755 index 0000000..ea92a6d --- /dev/null +++ b/se/sics/mspsim/platform/ti/Trxeb2520Node.java @@ -0,0 +1,111 @@ +package se.sics.mspsim.platform.ti; + +import java.io.IOException; + +import se.sics.mspsim.chip.CC2520; +import se.sics.mspsim.config.MSP430f5437Config; +import se.sics.mspsim.core.EmulationException; +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.util.ArgumentManager; + +public class Trxeb2520Node extends GenericNode implements PortListener, USARTListener { + + /* TrxEB + CC2520EM 2.1. + * Assuming non-default GPIO configuration (see trxeb2520 platform) */ + public static final int CC2520_FIFOP = 4; /* GPIO0 - 1.4 */ + public static final int CC2520_FIFO = 4; /* GPIO1 - 3.4 */ + public static final int CC2520_CCA = 3; /* GPIO3 - 1.3 */ + public static final int CC2520_SFD = 2; /* GPIO4 - 1.2 */ + + /* Output: VREG_EN to CC2520 */ + public static final int CC2520_VREG = (1 << 7); /* 1.7 */ + /* Output: SPI Chip Select (CS_N) */ + public static final int CC2520_CHIP_SELECT = (1 << 0); /* 3.0 */ + public static final int CC2520_RESET = (1 << 0); /* 8.0 */ + + IOPort port1; + IOPort port3; + IOPort port4; + IOPort port5; + IOPort port7; + IOPort port8; + + public CC2520 radio; + + public Trxeb2520Node() { + super("Trxeb2520", new MSP430f5437Config()); + } + + public void dataReceived(USARTSource source, int data) { + radio.dataReceived(source, data); + + /* if nothing selected, just write back a random byte to these devs */ + if (!radio.getChipSelect()) { + source.byteReceived(0); + } + } + + public void portWrite(IOPort source, int data) { + if (source == port1) { + radio.setVRegOn((data & CC2520_VREG) != 0); + } else if (source == port3) { + radio.setChipSelect((data & CC2520_CHIP_SELECT) == 0); + } else if (source == port8) { +// if ((data & CC2520_RESET) != 0) { +// radio.notifyReset(); +// } + } + } + + 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) { + radio = new CC2520(cpu); + radio.setGPIO(0, port1, CC2520_FIFOP); + radio.setGPIO(1, port3, CC2520_FIFO); + radio.setGPIO(3, port1, CC2520_CCA); + radio.setGPIO(4, port1, CC2520_SFD); + ((USARTSource) usart0).addUSARTListener(this); + } else { + throw new EmulationException("Error creating Trxeb2520Node: no USCI B0"); + } + + IOUnit usart = cpu.getIOUnit("USCI A1"); + if (usart instanceof USARTSource) { + registry.registerComponent("serialio", usart); + } + } + + public void setupNode() { + setupNodePorts(); + } + + public int getModeMax() { + return 0; + } + + public static void main(String[] args) throws IOException { + Trxeb2520Node node = new Trxeb2520Node(); + ArgumentManager config = new ArgumentManager(); + config.handleArguments(args); + node.setupArgs(config); + } +} From b0a7372c95cdc720f232c49530b9c95b94a328c7 Mon Sep 17 00:00:00 2001 From: Adam Dunkels Date: Thu, 29 Aug 2013 20:10:36 +0200 Subject: [PATCH 09/14] Support for the exp and cc430 platforms --- se/sics/mspsim/Main.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/se/sics/mspsim/Main.java b/se/sics/mspsim/Main.java index adbfb4b..7270aac 100644 --- a/se/sics/mspsim/Main.java +++ b/se/sics/mspsim/Main.java @@ -75,6 +75,15 @@ public class Main { if ("exp5438".equals(platform)) { return "se.sics.mspsim.platform.ti.Exp5438Node"; } + if ("exp1101".equals(platform)) { + return "se.sics.mspsim.platform.ti.Exp1101Node"; + } + if ("exp1120".equals(platform)) { + return "se.sics.mspsim.platform.ti.Exp1120Node"; + } + if ("cc430".equals(platform)) { + return "se.sics.mspsim.platform.ti.CC430Node"; + } // Try to guess the node type. return "se.sics.mspsim.platform." + platform + '.' + Character.toUpperCase(platform.charAt(0)) From d59a319f17388179d83f342fa5d733b1a071a2e5 Mon Sep 17 00:00:00 2001 From: Adam Dunkels Date: Thu, 29 Aug 2013 20:14:39 +0200 Subject: [PATCH 10/14] Improved emulation support for the CC2520 transceiver --- se/sics/mspsim/chip/CC2520.java | 64 +++++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/se/sics/mspsim/chip/CC2520.java b/se/sics/mspsim/chip/CC2520.java index 69c49aa..ab3f87c 100644 --- a/se/sics/mspsim/chip/CC2520.java +++ b/se/sics/mspsim/chip/CC2520.java @@ -521,6 +521,13 @@ public class CC2520 extends Radio802154 implements USARTListener, SPIData { } } + public int getFooterLength() { + if (autoCRC) { + return 2; + } + return 0; + } + private void reset() { // FCF max fram version = 3 and frame filtering enabled memory[REG_FRMFILT0] = 0x0d; @@ -837,20 +844,24 @@ public class CC2520 extends Radio802154 implements USARTListener, SPIData { // Here we check the CRC of the packet! //System.out.println("Reading from " + ((rxfifoWritePos + 128 - 2) & 127)); - int crc = rxFIFO.get(-2) << 8; - crc += rxFIFO.get(-1); //memory[RAM_RXFIFO + ((rxfifoWritePos + 128 - 1) & 127)]; + if (autoCRC) { + int crc = rxFIFO.get(-2) << 8; + crc += rxFIFO.get(-1); //memory[RAM_RXFIFO + ((rxfifoWritePos + 128 - 1) & 127)]; - crcOk = crc == rxCrc.getCRCBitrev(); - if (DEBUG && !crcOk) { - log("CRC not OK: recv:" + Utils.hex16(crc) + " calc: " + Utils.hex16(rxCrc.getCRCBitrev())); + crcOk = crc == rxCrc.getCRCBitrev(); + if (DEBUG && !crcOk) { + log("CRC not OK: recv:" + Utils.hex16(crc) + " calc: " + Utils.hex16(rxCrc.getCRCBitrev())); + } + // Should take a RSSI value as input or use a set-RSSI value... + rxFIFO.set(-2, memory[REG_RSSI] & 0xff); + rxFIFO.set(-1, (corrval & 0x7F) | (crcOk ? 0x80 : 0)); + // memory[RAM_RXFIFO + ((rxfifoWritePos + 128 - 2) & 127)] = ; + // // Set CRC ok and add a correlation - TODO: fix better correlation value!!! + // memory[RAM_RXFIFO + ((rxfifoWritePos + 128 - 1) & 127)] = 37 | + // (crcOk ? 0x80 : 0); + } else { + crcOk = true; } - // Should take a RSSI value as input or use a set-RSSI value... - rxFIFO.set(-2, memory[REG_RSSI] & 0xff); - rxFIFO.set(-1, (corrval & 0x7F) | (crcOk ? 0x80 : 0)); - // memory[RAM_RXFIFO + ((rxfifoWritePos + 128 - 2) & 127)] = ; - // // Set CRC ok and add a correlation - TODO: fix better correlation value!!! - // memory[RAM_RXFIFO + ((rxfifoWritePos + 128 - 1) & 127)] = 37 | - // (crcOk ? 0x80 : 0); /* set FIFOP only if this is the first received packet - e.g. if rxfifoLen is at most rxlen + 1 * TODO: check what happens when rxfifoLen < rxlen - e.g we have been reading before FIFOP */ @@ -910,6 +921,15 @@ public class CC2520 extends Radio802154 implements USARTListener, SPIData { // + " CCAMUX: " + (memory[address] & CCAMUX)); // updateCCA(); // break; + case REG_GPIOCTRL0: + /* + * XXX TODO Implement support for GPIO control. Below example code + * demonstrates how GPIO0 is set to fifop functionality (0x28). + */ + if (data == 0x28) { + fifopGPIO = gpio[0]; + } + break; case REG_FSCTRL: { ChannelListener listener = this.channelListener; if (listener != null) { @@ -926,6 +946,10 @@ public class CC2520 extends Radio802154 implements USARTListener, SPIData { } int readMemory(int address) { + switch(address) { + case REG_RXFIFOCNT: + return rxFIFO.length(); + } return memory[address]; } @@ -1084,14 +1108,18 @@ public class CC2520 extends Radio802154 implements USARTListener, SPIData { private void txNext() { if(txfifoPos <= memory[RAM_TXFIFO]) { int len = memory[RAM_TXFIFO] & 0xff; - if (txfifoPos == len - 1) { - txCrc.setCRC(0); - for (int i = 1; i < len - 1; i++) { - txCrc.addBitrev(memory[RAM_TXFIFO + i] & 0xff); + + if (autoCRC) { + if (txfifoPos == len - 1) { + txCrc.setCRC(0); + for (int i = 1; i < len - 1; i++) { + txCrc.addBitrev(memory[RAM_TXFIFO + i] & 0xff); + } + memory[RAM_TXFIFO + len - 1] = txCrc.getCRCHi(); + memory[RAM_TXFIFO + len] = txCrc.getCRCLow(); } - memory[RAM_TXFIFO + len - 1] = txCrc.getCRCHi(); - memory[RAM_TXFIFO + len] = txCrc.getCRCLow(); } + if (txfifoPos > 0x7f) { logw(WarningType.EXECUTION, "**** Warning - packet size too large - repeating packet bytes txfifoPos: " + txfifoPos); } From b5985a4f47fc6e8bbc65caab02765cc1d0dc1ea4 Mon Sep 17 00:00:00 2001 From: Adam Dunkels Date: Thu, 29 Aug 2013 20:16:40 +0200 Subject: [PATCH 11/14] Improved handling of the stack idenfitication at end of memory that works better for mspgcc 4.7.x --- se/sics/mspsim/util/ELF.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/se/sics/mspsim/util/ELF.java b/se/sics/mspsim/util/ELF.java index e4b0e01..fbde95d 100644 --- a/se/sics/mspsim/util/ELF.java +++ b/se/sics/mspsim/util/ELF.java @@ -374,6 +374,7 @@ public class ELF { public MapTable getMap() { MapTable map = new MapTable(); int sAddrHighest = -1; + boolean foundEnd = false; ELFSection name = sections[symTable.link]; int len = symTable.size; @@ -418,13 +419,14 @@ public class ELF { if (sAddr > 0 && sAddr < 0x100000) { String symbolName = sn; - if (sAddr < 0x5c00 && sAddr > sAddrHighest) { + if (sAddr < 0x5c00 && sAddr > sAddrHighest && !sn.equals("__stack")) { sAddrHighest = sAddr; } // if (bind == ELFSection.SYMBIND_LOCAL) { // symbolName += " (" + currentFile + ')'; // } if ("_end".equals(symbolName)) { + foundEnd = true; map.setHeapStart(sAddr); } else if ("__stack".equals(symbolName)){ map.setStackStart(sAddr); @@ -455,10 +457,10 @@ public class ELF { addr += symTable.getEntrySize(); } - if (map.getHeapStart() <= 0 && sAddrHighest > 0) { - System.err.printf("Warning: Unable to parse _end symbol. I'm guessing that heap starts at 0x%05x\n", sAddrHighest); - map.setHeapStart(sAddrHighest); - } + if (!foundEnd && sAddrHighest > 0) { + System.out.printf("Warning: Unable to parse _end symbol. I'm guessing that heap starts at 0x%05x\n", sAddrHighest); + map.setHeapStart(sAddrHighest); + } return map; } From 8efdbba5c5fa004cf64617f50d6eed4dc4f5556b Mon Sep 17 00:00:00 2001 From: Adam Dunkels Date: Thu, 29 Aug 2013 20:17:01 +0200 Subject: [PATCH 12/14] Bugfix: only send i2c acks if i2c mode is enabled --- se/sics/mspsim/core/GenericUSCI.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/se/sics/mspsim/core/GenericUSCI.java b/se/sics/mspsim/core/GenericUSCI.java index 50f4ec1..017f9cd 100644 --- a/se/sics/mspsim/core/GenericUSCI.java +++ b/se/sics/mspsim/core/GenericUSCI.java @@ -373,7 +373,7 @@ public class GenericUSCI extends IOUnit implements DMATrigger, USARTSource { /* For timing issues we have to send the ACK after reading the * register */ - if (!i2cTransmitter) { + if (i2cEnabled && !i2cTransmitter) { txBuffer.add(I2CData.ACK); stat |= USCI_BUSY; if (!transmitting) { From bda5832550246bd288fb41dc45a3d1182ae0dcdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20L=C3=A9one?= Date: Fri, 13 Dec 2013 13:41:52 +0100 Subject: [PATCH 13/14] Add jar ignore --- .gitignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 586ffec..1465e2b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,8 +3,8 @@ *.class # Package Files # -*.jar +./*.jar *.war *.ear -.DS_Store \ No newline at end of file +.DS_Store From 79cbf9f00c8547bd36e6f9648f0f1b6a6e40d9a5 Mon Sep 17 00:00:00 2001 From: Adam Dunkels Date: Tue, 24 Mar 2015 15:17:50 +0100 Subject: [PATCH 14/14] Updated the CC1101 and the CC1120 emulation code to the latest version --- se/sics/mspsim/chip/CC1101.java | 124 ++++++++++++++++++++----- se/sics/mspsim/chip/CC1120.java | 160 ++++++++++++++++++++++++++------ 2 files changed, 231 insertions(+), 53 deletions(-) diff --git a/se/sics/mspsim/chip/CC1101.java b/se/sics/mspsim/chip/CC1101.java index fa8d85d..1905e06 100644 --- a/se/sics/mspsim/chip/CC1101.java +++ b/se/sics/mspsim/chip/CC1101.java @@ -39,7 +39,7 @@ import se.sics.mspsim.core.USARTListener; import se.sics.mspsim.core.USARTSource; public class CC1101 extends Radio802154 implements USARTListener { - protected boolean DEBUG = true; + protected boolean DEBUG = false; /* cc1101-const.h: Configuration registers */ public static final int CC1101_IOCFG1 = 0x01; @@ -133,6 +133,9 @@ public class CC1101 extends Radio802154 implements USARTListener { public static final int CC1101_SWORRST = 0x3C; public static final int CC1101_SNOP = 0x3D; + public static final int CC1101_PKTSTATUS_CS_BIT = (1 << 6); + public static final int CC1101_PKTSTATUS_CCA_BIT = (1 << 4); + public static enum CC1101RadioState { CC1101_STATE_SLEEP(0x00)/* 0 */, CC1101_STATE_IDLE(0x01), @@ -174,15 +177,18 @@ public class CC1101 extends Radio802154 implements USARTListener { } }; - public final static double SYMBOL_PERIOD = 0.016; /* TODO XXX 16 us */ public final static double FREQUENCY_CHANNEL_0 = 902; /* MHz */ public final static double FREQUENCY_CHANNEL_WIDTH = 0.125; /* MHz */ public final static int CCA_THRESHOLD = -95; + private boolean triggerGDO0onSynch = false; + private boolean triggerGDO0onFifoThreshold = true; + private StateListener stateListener = null; private ReceiverListener receiverListener = null; - + + private boolean currentRssiValid; private int currentRssiReg = 0; private CC1101RadioState state = null; @@ -207,6 +213,24 @@ public class CC1101 extends Radio802154 implements USARTListener { reset(); } + public long getBitRate() { + /* This function returns the current bit rate of the radio. It + should use the CC1101 configuration registers to figure out + the actual bit rate, but this code simply checks for two + specific configurations that correspond to known bit + rates. */ + if(registers[CC1101_MDMCFG3] == 0xf8) { + return 50000; + } else if(registers[CC1101_MDMCFG3] == 0x3b) { + return 250000; + } + return 250000; + } + + public double getInterByteDelayMs() { + return 1000.0 / (getBitRate() / 8.0); + } + public void log(String str) { if (DEBUG) { System.out.println(str); @@ -233,7 +257,38 @@ public class CC1101 extends Radio802154 implements USARTListener { break; case CC1101_SRX: - setState(CC1101RadioState.CC1101_STATE_RX); + if(getState() == CC1101RadioState.CC1101_STATE_IDLE || + getState() == CC1101RadioState.CC1101_STATE_SLEEP) { + log("CC1101 from idle to rx, should wait"); + TimeEvent goToRX = new TimeEvent(0, "CC1101 go to RX") { + public void execute(long t) { + if(getState() == CC1101RadioState.CC1101_STATE_RX) { + /* Radio already in RX, ignore */ + return; + } + rxfifo.clear(); + rxExpectedLen = -1; + rxGotSynchByte = false; + setGDO0(false); + setState(CC1101RadioState.CC1101_STATE_RX); + } + }; + int RXTIME = 190; + cpu.scheduleTimeEventMillis(goToRX, RXTIME / 1000.0); + + TimeEvent rssiValid = new TimeEvent(0, "CC1101 set RSSI valid") { + public void execute(long t) { + log("RSSI is now valid"); + currentRssiValid = true; + } + }; + log("RSSI is not valid"); + int RSSITIME = 380; + cpu.scheduleTimeEventMillis(rssiValid, RSSITIME / 1000.0); + // setState(CC1101RadioState.CC1101_STATE_RX); + } else { + setStateRX(); + } break; case CC1101_STX: @@ -248,6 +303,7 @@ public class CC1101 extends Radio802154 implements USARTListener { case CC1101_SIDLE: setState(CC1101RadioState.CC1101_STATE_IDLE); + currentRssiValid = false; break; case CC1101_SAFC: @@ -259,11 +315,11 @@ public class CC1101 extends Radio802154 implements USARTListener { break; case CC1101_SPWD: - log("CC1101_SPWD almost implemented"); + // log("CC1101_SPWD almost implemented"); /* TODO XXX * Wait until CS is de-asserted. (We should at least wait * receiving or transmitting.)*/ - setState(CC1101RadioState.CC1101_STATE_IDLE); + setState(CC1101RadioState.CC1101_STATE_SLEEP); break; case CC1101_SFRX: @@ -285,7 +341,7 @@ public class CC1101 extends Radio802154 implements USARTListener { break; case CC1101_SNOP: - log("CC1101_SNOP not implemented"); + // log("CC1101_SNOP not implemented"); break; default: @@ -398,6 +454,8 @@ public class CC1101 extends Radio802154 implements USARTListener { public int getReg(int address) { /* MSP430Core.profiler.printStackTrace(System.out); */ switch (address) { + case CC1101_CHANNR: + return channel; case CC1101_MARCSTATE: return getMarcstate(); case CC1101_RXBYTES: @@ -407,13 +465,14 @@ public class CC1101 extends Radio802154 implements USARTListener { /*log("getReg(CC1101_TXBYTES) " + txfifo.size());*/ return txfifo.size(); case CC1101_PKTSTATUS: - int status; - final int CCA_BIT = (1<<4); - if(currentRssiReg < CCA_THRESHOLD) { - status = CCA_BIT; - } else { - status = 0; - } + int status = 0; + if(currentRssiValid) { + if(currentRssiReg < CCA_THRESHOLD) { + status |= CC1101_PKTSTATUS_CCA_BIT; + } else { + status |= CC1101_PKTSTATUS_CS_BIT; + } + } return status; case CC1101_RSSI: return currentRssiReg; @@ -421,6 +480,9 @@ public class CC1101 extends Radio802154 implements USARTListener { if (rxfifo.size() > 0) { int ret = (int) rxfifo.remove(0); /*printRXFIFO();*/ + if (triggerGDO0onFifoThreshold && rxfifo.size() == 0) { + setGDO0(false); + } return ret; } System.err.println("Warning: reading from empty RXFIFO"); @@ -468,7 +530,7 @@ public class CC1101 extends Radio802154 implements USARTListener { if (rfListener != null) { rfListener.receivedByte((byte) (0xaa)); } - cpu.scheduleTimeEventMillis(sendEvent, SYMBOL_PERIOD * 2); + cpu.scheduleTimeEventMillis(sendEvent, getInterByteDelayMs()); return; } /* Send NUM_SYNCH-1 synch bytes */ @@ -477,7 +539,7 @@ public class CC1101 extends Radio802154 implements USARTListener { if (rfListener != null) { rfListener.receivedByte((byte) (SYNCH_BYTE_LAST + 1)); } - cpu.scheduleTimeEventMillis(sendEvent, SYMBOL_PERIOD * 2); + cpu.scheduleTimeEventMillis(sendEvent, getInterByteDelayMs()); return; } /* Send last synch byte */ @@ -486,7 +548,7 @@ public class CC1101 extends Radio802154 implements USARTListener { if (rfListener != null) { rfListener.receivedByte((byte) (SYNCH_BYTE_LAST)); } - cpu.scheduleTimeEventMillis(sendEvent, SYMBOL_PERIOD * 2); + cpu.scheduleTimeEventMillis(sendEvent, getInterByteDelayMs()); txSentSynchByte = true; return; @@ -518,7 +580,7 @@ public class CC1101 extends Radio802154 implements USARTListener { rfListener.receivedByte((byte) (0xee)); } txSentFirstCRC = true; - cpu.scheduleTimeEventMillis(sendEvent, SYMBOL_PERIOD * 2); + cpu.scheduleTimeEventMillis(sendEvent, getInterByteDelayMs()); return; } @@ -528,7 +590,8 @@ public class CC1101 extends Radio802154 implements USARTListener { if (rfListener != null) { rfListener.receivedByte((byte) 0); } - cpu.scheduleTimeEventMillis(sendEvent, SYMBOL_PERIOD * 2); + + cpu.scheduleTimeEventMillis(sendEvent, getInterByteDelayMs()); setState(CC1101RadioState.CC1101_STATE_TXFIFO_UNDERFLOW); return; } @@ -537,7 +600,7 @@ public class CC1101 extends Radio802154 implements USARTListener { rfListener.receivedByte((byte) (txfifo.get(0).intValue())); } txfifo.remove(0); - cpu.scheduleTimeEventMillis(sendEvent, SYMBOL_PERIOD * 2); + cpu.scheduleTimeEventMillis(sendEvent, getInterByteDelayMs()); /*printTXFIFO();*/ } @@ -643,11 +706,16 @@ public class CC1101 extends Radio802154 implements USARTListener { boolean rxGotSynchByte = false; private int rxExpectedLen = -1; public void receivedByte(byte data) { + if(state != CC1101RadioState.CC1101_STATE_RX) { + return; + } if (!rxGotSynchByte) { /* Await synch byte */ if (data == SYNCH_BYTE_LAST) { rxGotSynchByte = true; - setGDO0(true); + if(triggerGDO0onSynch) { + setGDO0(true); + } } return; } @@ -663,7 +731,16 @@ public class CC1101 extends Radio802154 implements USARTListener { rxGotSynchByte = false; } - rxfifo.add(data); + if (rxfifo.size() < 64) { + rxfifo.add(data); + + if (triggerGDO0onFifoThreshold && rxfifo.size() >= 3 && rxExpectedLen > 0) { + setGDO0(true); + } + } else { + log("rxfifo overflow " + rxfifo.size()); + setState(CC1101RadioState.CC1101_STATE_RXFIFO_OVERFLOW); + } /*printRXFIFO();*/ } @@ -749,10 +826,11 @@ public class CC1101 extends Radio802154 implements USARTListener { void setStateRX() { setState(CC1101RadioState.CC1101_STATE_RX); + currentRssiValid = true; } void reset() { - setState(CC1101RadioState.CC1101_STATE_IDLE); + setState(CC1101RadioState.CC1101_STATE_SLEEP); registers[CC1101_PARTNUM] = 0; registers[CC1101_VERSION] = 6; diff --git a/se/sics/mspsim/chip/CC1120.java b/se/sics/mspsim/chip/CC1120.java index 54d2774..8a3b22e 100755 --- a/se/sics/mspsim/chip/CC1120.java +++ b/se/sics/mspsim/chip/CC1120.java @@ -252,6 +252,9 @@ public class CC1120 extends Radio802154 implements USARTListener { public final static int CCA_THRESHOLD = -95; + private boolean triggerGDO0onSynch = false; + private boolean triggerGDO0onFifoThreshold = false; + public static enum CC1120RadioState { CC1120_STATE_SLEEP(0b00000)/* 0 */, CC1120_STATE_IDLE(0b00001), @@ -297,7 +300,7 @@ public class CC1120 extends Radio802154 implements USARTListener { protected boolean DEBUG = false; - public final static double SYMBOL_PERIOD = 0.016; /* TODO XXX 16 us */ + public final static double BITRATE_BYTE_DURATION = 0.16; /* ms. Duration per byte transmitted, corresponds to 50kbit/s */ public final static double FREQUENCY_CHANNEL_0 = 902; /* MHz */ public final static double FREQUENCY_CHANNEL_WIDTH = 0.125; /* MHz */ @@ -355,7 +358,42 @@ public class CC1120 extends Radio802154 implements USARTListener { break; case CC1120_SRX: - setState(CC1120RadioState.CC1120_STATE_RX); + + if(getState() == CC1120RadioState.CC1120_STATE_IDLE || + getState() == CC1120RadioState.CC1120_STATE_SLEEP) { + TimeEvent goToRX = new TimeEvent(0, "CC1120 go to RX") { + public void execute(long t) { + if(getState() == CC1120RadioState.CC1120_STATE_RX) { + /* Radio already in RX, ignore */ + return; + } + rxfifo.clear(); + rxExpectedLen = -1; + rxGotSynchByte = false; + setGDO0(false); + setState(CC1120RadioState.CC1120_STATE_RX); + } + }; + + /* The time to activate RX depends on SETTLING_CFG's FS_AUTOCAL settings. + * Calibrated from trxeb1120 platform. */ + double stateDelay; + int settling = getRegister(CC1120_SETTLING_CFG); + settling = (settling >> 3) & 0b11; /* bit 4:3 */ + if (settling == 0) { + stateDelay = 0.20; + } else if (settling == 1) { + stateDelay = 0.50; + } else { + /* not implemented: assuming calibration */ + stateDelay = 0.50; + } + cpu.scheduleTimeEventMillis(goToRX, stateDelay); + + } else { + setStateRX(); + } + break; case CC1120_STX: @@ -381,7 +419,7 @@ public class CC1120 extends Radio802154 implements USARTListener { break; case CC1120_SPWD: - System.out.println("CC1120_SPWD almost implemented"); + /*System.out.println("CC1120_SPWD almost implemented");*/ /* TODO XXX * Wait until CS is de-asserted. (We should at least wait until radio is done * receiving or transmitting.)*/ @@ -526,6 +564,9 @@ public class CC1120 extends Radio802154 implements USARTListener { return; } int setReg(int address, int data, boolean extended) { + /*System.err.println(String.format("setReg(0x%02x, %s) 0x%02x", address, + extended ? "extended" : "normal", data));*/ + switch (address) { case CC1120_TXFIFO: txfifo.add((byte) data); @@ -540,16 +581,45 @@ public class CC1120 extends Radio802154 implements USARTListener { case CC1120_FREQ0: nextFreq0 = data; return 0; + case CC1120_IOCFG0: + if (data == 65 || data == 1) { + /* CC1120_SETTING_IOCFG0 (IOCFG_GPIO_CFG_RXFIFO_THR_PKT) + * or + * CC1120_SETTING_IOCFG0 (IOCFG_GPIO_CFG_RXFIFO_THR_PKT | IOCFG_GPIO_CFG_INVERT) */ + triggerGDO0onFifoThreshold = true; + } else { + /* Assuming: + * CC1120_SETTING_IOCFG0 (IOCFG_GPIO_CFG_PKT_SYNC_RXTX | IOCFG_GPIO_CFG_INVERT) */ + triggerGDO0onSynch = true; + } + return 0; } - System.out.println(String.format("setReg(0x%02x, %s) 0x%02x", address, - extended ? "extended" : "normal", data)); if (extended) { int oldValue = extendedRegisters[address]; extendedRegisters[address] = data; configurationChanged(address, oldValue, data); return oldValue; } + + switch (address) { + case CC1120_DRATE0: + if (data != 0x99) { + System.err.println("Warning: incompatible CC1120 data rate. Only 50kbit/s (0x99) is currently supported. CC1120_DRATE0: " + Integer.toHexString(data)); + } + return 0; + case CC1120_DRATE1: + if (data != 0x99) { + System.err.println("Warning: incompatible CC1120 data rate. Only 50kbit/s (0x99) is currently supported. CC1120_DRATE1: " + Integer.toHexString(data)); + } + return 0; + case CC1120_DRATE2: + if (data != 0x99) { + System.err.println("Warning: incompatible CC1120 data rate. Only 50kbit/s (0x99) is currently supported. CC1120_DRATE2: " + Integer.toHexString(data)); + } + return 0; + } + int oldValue = registers[address]; registers[address] = data; configurationChanged(address, oldValue, data); @@ -572,7 +642,7 @@ public class CC1120 extends Radio802154 implements USARTListener { ret += (0b1111 & currentRssiReg0); /* RSSI_3_0 */ ret = ret << 1; - if(getRSSI() < CCA_THRESHOLD) { + if(currentRssi > CCA_THRESHOLD) { ret += 1; /* Carrier detected */ } else { ret += 0; /* No carrier detected */ @@ -593,18 +663,23 @@ public class CC1120 extends Radio802154 implements USARTListener { if (rxfifo.size() > 0) { int ret = (int) rxfifo.remove(0); /* printRXFIFO(); */ + + if (triggerGDO0onFifoThreshold && rxfifo.size() == 0) { + setGDO0(false); + } return ret; } - System.err.println("Warning: reading from empty RXFIFO"); + + /* RXFIFO underflow */ + setState(CC1120RadioState.CC1120_STATE_RX_FIFO_ERR); return -1; } if (address != CC1120.CC1120_MARCSTATE) { - System.out - .println(String.format("getReg(0x%02x, %s) 0x%02x", + /*System.out.println(String.format("getReg(0x%02x, %s) 0x%02x", address, extended ? "extended" : "normal", extended ? extendedRegisters[address] - : registers[address])); + : registers[address]));*/ } if (extended) { @@ -632,6 +707,7 @@ public class CC1120 extends Radio802154 implements USARTListener { public static final int NUM_PREAMBLE = 4; public static final int NUM_SYNCH = 4; public static final byte SYNCH_BYTE_LAST = (byte) 0xDE; + private boolean txPreambleDelay = false; private boolean txSentSynchByte = false; private int txSendSynchByteCnt = 0; private boolean txSentFirstCRC = false; @@ -641,6 +717,13 @@ public class CC1120 extends Radio802154 implements USARTListener { return; } + /* Delay */ + if (!txPreambleDelay) { + cpu.scheduleTimeEventMillis(sendEvent, 0.75); + txPreambleDelay = true; + return; + } + /* Send preamble and synch bytes */ if (!txSentSynchByte) { /* Send NUM_PREAMBLE preamble bytes */ @@ -649,7 +732,7 @@ public class CC1120 extends Radio802154 implements USARTListener { if (rfListener != null) { rfListener.receivedByte((byte) (0xaa)); } - cpu.scheduleTimeEventMillis(sendEvent, SYMBOL_PERIOD * 2); + cpu.scheduleTimeEventMillis(sendEvent, BITRATE_BYTE_DURATION); return; } /* Send NUM_SYNCH-1 synch bytes */ @@ -658,7 +741,7 @@ public class CC1120 extends Radio802154 implements USARTListener { if (rfListener != null) { rfListener.receivedByte((byte) (SYNCH_BYTE_LAST + 1)); } - cpu.scheduleTimeEventMillis(sendEvent, SYMBOL_PERIOD * 2); + cpu.scheduleTimeEventMillis(sendEvent, BITRATE_BYTE_DURATION); return; } /* Send last synch byte */ @@ -667,7 +750,7 @@ public class CC1120 extends Radio802154 implements USARTListener { if (rfListener != null) { rfListener.receivedByte((byte) (SYNCH_BYTE_LAST)); } - cpu.scheduleTimeEventMillis(sendEvent, SYMBOL_PERIOD * 2); + cpu.scheduleTimeEventMillis(sendEvent, BITRATE_BYTE_DURATION); txSentSynchByte = true; return; @@ -685,6 +768,7 @@ public class CC1120 extends Radio802154 implements USARTListener { System.out.println("Warning: TXFIFO not empty after sending CRC bytes"); } setStateRX(); + txPreambleDelay = false; txSentSynchByte = false; txSendSynchByteCnt = 0; txSentFirstCRC = false; @@ -699,7 +783,7 @@ public class CC1120 extends Radio802154 implements USARTListener { rfListener.receivedByte((byte) (0xee)); } txSentFirstCRC = true; - cpu.scheduleTimeEventMillis(sendEvent, SYMBOL_PERIOD * 2); + cpu.scheduleTimeEventMillis(sendEvent, BITRATE_BYTE_DURATION); return; } @@ -709,7 +793,7 @@ public class CC1120 extends Radio802154 implements USARTListener { if (rfListener != null) { rfListener.receivedByte((byte) 0); } - cpu.scheduleTimeEventMillis(sendEvent, SYMBOL_PERIOD * 2); + cpu.scheduleTimeEventMillis(sendEvent, BITRATE_BYTE_DURATION); return; } @@ -717,7 +801,7 @@ public class CC1120 extends Radio802154 implements USARTListener { rfListener.receivedByte((byte) (txfifo.get(0).intValue())); } txfifo.remove(0); - cpu.scheduleTimeEventMillis(sendEvent, SYMBOL_PERIOD * 2); + cpu.scheduleTimeEventMillis(sendEvent, BITRATE_BYTE_DURATION); /* printTXFIFO(); */ } @@ -777,11 +861,11 @@ public class CC1120 extends Radio802154 implements USARTListener { } boolean receiverOn = false; - boolean setState(CC1120RadioState newState) { + boolean setState(final CC1120RadioState newState) { if (newState != CC1120RadioState.CC1120_STATE_IDLE && newState != CC1120RadioState.CC1120_STATE_RX && newState != CC1120RadioState.CC1120_STATE_TX) { - System.out.println("setState(" + newState + ")"); + /*System.out.println("setState(" + newState + ")");*/ } if (changeFrequencyNextState @@ -807,12 +891,13 @@ public class CC1120 extends Radio802154 implements USARTListener { /* Notify state listener */ if (stateListener != null) { - stateListener.newState(state); + stateListener.newState(state); } if (receiverListener != null && isReadyToReceive() != receiverOn) { - receiverOn = isReadyToReceive(); - receiverListener.newState(receiverOn); + receiverOn = isReadyToReceive(); + receiverListener.newState(receiverOn); } + return true; } @@ -823,7 +908,10 @@ public class CC1120 extends Radio802154 implements USARTListener { /* Await synch byte */ if (data == SYNCH_BYTE_LAST) { rxGotSynchByte = true; - setGDO0(true); + + if(triggerGDO0onSynch) { + setGDO0(true); + } } return; } @@ -834,12 +922,22 @@ public class CC1120 extends Radio802154 implements USARTListener { rxExpectedLen--; if (rxExpectedLen == 0) { - setGDO0(false); + setGDO0(false); /* triggerGDO0onFifoThreshold, triggerGDO0onSynch */ rxExpectedLen = -1; rxGotSynchByte = false; } - rxfifo.add(data); + if (rxfifo.size() < 128) { + /* printRXFIFO(); */ + rxfifo.add(data); + + if (triggerGDO0onFifoThreshold && rxfifo.size() >= 3 && rxExpectedLen > 0) { + setGDO0(true); + } + } else { + /* RXFIFO overflow */ + setState(CC1120RadioState.CC1120_STATE_RX_FIFO_ERR); + } /* printRXFIFO(); */ } @@ -855,12 +953,14 @@ public class CC1120 extends Radio802154 implements USARTListener { registers[register] = data; } - public void setRSSI(int power) { - currentRssiReg1 = 0xff&power; - } - public int getRSSI() { - return currentRssiReg1; - } + private int currentRssi = -100; + public void setRSSI(int power) { + currentRssi = power; + currentRssiReg1 = 0xff&power; + } + public int getRSSI() { + return currentRssiReg1; + } public int getActiveFrequency() { return (int) (1000 * Math.round(frequency)); /* kHz */