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 diff --git a/images/cc430.jpg b/images/cc430.jpg new file mode 100755 index 0000000..19d84c9 Binary files /dev/null and b/images/cc430.jpg differ 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)) diff --git a/se/sics/mspsim/chip/CC1101.java b/se/sics/mspsim/chip/CC1101.java new file mode 100644 index 0000000..1905e06 --- /dev/null +++ b/se/sics/mspsim/chip/CC1101.java @@ -0,0 +1,905 @@ +/* + * 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 = false; + + /* 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 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), + 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 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; + + 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 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); + } + } + + 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: + 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: + 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); + currentRssiValid = false; + 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_SLEEP); + 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_CHANNR: + return channel; + 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 = 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; + case CC1101_RXFIFO: + 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"); + 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, getInterByteDelayMs()); + 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, getInterByteDelayMs()); + return; + } + /* Send last synch byte */ + if (txSendSynchByteCnt < NUM_PREAMBLE + NUM_SYNCH) { + txSendSynchByteCnt++; + if (rfListener != null) { + rfListener.receivedByte((byte) (SYNCH_BYTE_LAST)); + } + cpu.scheduleTimeEventMillis(sendEvent, getInterByteDelayMs()); + + 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, getInterByteDelayMs()); + 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, getInterByteDelayMs()); + setState(CC1101RadioState.CC1101_STATE_TXFIFO_UNDERFLOW); + return; + } + + if (rfListener != null) { + rfListener.receivedByte((byte) (txfifo.get(0).intValue())); + } + txfifo.remove(0); + cpu.scheduleTimeEventMillis(sendEvent, getInterByteDelayMs()); + + /*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(state != CC1101RadioState.CC1101_STATE_RX) { + return; + } + if (!rxGotSynchByte) { + /* Await synch byte */ + if (data == SYNCH_BYTE_LAST) { + rxGotSynchByte = true; + if(triggerGDO0onSynch) { + setGDO0(true); + } + } + return; + } + + if (rxExpectedLen < 0) { + rxExpectedLen = 1/*len*/ + (int) data/*payload*/ + 2/*CRC*/; + } + + rxExpectedLen--; + if (rxExpectedLen == 0) { + setGDO0(false); + rxExpectedLen = -1; + rxGotSynchByte = false; + } + + 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();*/ + } + + 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); + currentRssiValid = true; + } + + void reset() { + setState(CC1101RadioState.CC1101_STATE_SLEEP); + + 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; + } + +} diff --git a/se/sics/mspsim/chip/CC1120.java b/se/sics/mspsim/chip/CC1120.java new file mode 100755 index 0000000..8a3b22e --- /dev/null +++ b/se/sics/mspsim/chip/CC1120.java @@ -0,0 +1,1039 @@ +/* + * 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; + + private boolean triggerGDO0onSynch = false; + private boolean triggerGDO0onFifoThreshold = false; + + 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 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 */ + + 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: + + 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: + 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) { + /*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); + /* 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; + 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; + } + + 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); + 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(currentRssi > 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(); */ + + if (triggerGDO0onFifoThreshold && rxfifo.size() == 0) { + setGDO0(false); + } + return ret; + } + + /* 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", + 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 txPreambleDelay = false; + 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; + } + + /* Delay */ + if (!txPreambleDelay) { + cpu.scheduleTimeEventMillis(sendEvent, 0.75); + txPreambleDelay = true; + 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, BITRATE_BYTE_DURATION); + 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, BITRATE_BYTE_DURATION); + return; + } + /* Send last synch byte */ + if (txSendSynchByteCnt < NUM_PREAMBLE + NUM_SYNCH) { + txSendSynchByteCnt++; + if (rfListener != null) { + rfListener.receivedByte((byte) (SYNCH_BYTE_LAST)); + } + cpu.scheduleTimeEventMillis(sendEvent, BITRATE_BYTE_DURATION); + + 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(); + txPreambleDelay = false; + 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, BITRATE_BYTE_DURATION); + 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, BITRATE_BYTE_DURATION); + return; + } + + if (rfListener != null) { + rfListener.receivedByte((byte) (txfifo.get(0).intValue())); + } + txfifo.remove(0); + cpu.scheduleTimeEventMillis(sendEvent, BITRATE_BYTE_DURATION); + + /* 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(final 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; + + if(triggerGDO0onSynch) { + setGDO0(true); + } + } + return; + } + + if (rxExpectedLen < 0) { + rxExpectedLen = 1/*len*/ + (int) data/*payload*/ + 2/*CRC*/; + } + + rxExpectedLen--; + if (rxExpectedLen == 0) { + setGDO0(false); /* triggerGDO0onFifoThreshold, triggerGDO0onSynch */ + rxExpectedLen = -1; + rxGotSynchByte = false; + } + + 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(); */ + } + + 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; + } + + 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 */ + } + + 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(); + } + +} 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); } 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; + } +} 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/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) { 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 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); + } +} 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); + } +} 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; + } +} 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); + } +} 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; }