mirror of https://github.com/contiki-ng/mspsim
1640 lines
59 KiB
Java
1640 lines
59 KiB
Java
/**
|
|
* Copyright (c) 2012 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.
|
|
*
|
|
* -----------------------------------------------------------------
|
|
*
|
|
* CC2520
|
|
*
|
|
* Author : Joakim Eriksson, Niclas Finne
|
|
*/
|
|
|
|
package se.sics.mspsim.chip;
|
|
import se.sics.mspsim.core.*;
|
|
import se.sics.mspsim.core.EmulationLogger.WarningType;
|
|
import se.sics.mspsim.util.ArrayFIFO;
|
|
import se.sics.mspsim.util.CCITT_CRC;
|
|
import se.sics.mspsim.util.Utils;
|
|
|
|
public class CC2520 extends Radio802154 implements USARTListener, SPIData {
|
|
|
|
public static class GPIO {
|
|
private IOPort port;
|
|
private int pin;
|
|
|
|
boolean polarity = true;
|
|
boolean isActive;
|
|
|
|
public void setConfig(IOPort port, int pin) {
|
|
this.port = port;
|
|
this.pin = pin;
|
|
port.setPinState(pin, isActive == polarity ? IOPort.PinState.HI : IOPort.PinState.LOW);
|
|
}
|
|
|
|
public boolean isActive() {
|
|
return isActive;
|
|
}
|
|
|
|
public void setActive(boolean isActive) {
|
|
if (this.isActive != isActive) {
|
|
this.isActive = isActive;
|
|
if (port != null) {
|
|
port.setPinState(pin, isActive == polarity ? IOPort.PinState.HI : IOPort.PinState.LOW);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void setPolarity(boolean polarity) {
|
|
if (this.polarity != polarity) {
|
|
this.polarity = polarity;
|
|
if (port != null) {
|
|
port.setPinState(pin, isActive == polarity ? IOPort.PinState.HI : IOPort.PinState.LOW);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// FREG definitions (BSET/BCLR supported)
|
|
public final static int REG_FRMFILT0 = 0x000;
|
|
public final static int REG_FRMFILT1 = 0x001;
|
|
public final static int REG_SRCMATCH = 0x002;
|
|
public final static int REG_SRCSHORTEN0 = 0x004;
|
|
public final static int REG_SRCSHORTEN1 = 0x005;
|
|
public final static int REG_SRCSHORTEN2 = 0x006;
|
|
public final static int REG_SRCEXTEN0 = 0x008;
|
|
public final static int REG_SRCEXTEN1 = 0x009;
|
|
public final static int REG_SRCEXTEN2 = 0x00A;
|
|
public final static int REG_FRMCTRL0 = 0x00C;
|
|
public final static int REG_FRMCTRL1 = 0x00D;
|
|
public final static int REG_RXENABLE0 = 0x00E;
|
|
public final static int REG_RXENABLE1 = 0x00F;
|
|
public final static int REG_EXCFLAG0 = 0x010;
|
|
public final static int REG_EXCFLAG1 = 0x011;
|
|
public final static int REG_EXCFLAG2 = 0x012;
|
|
public final static int REG_EXCMASKA0 = 0x014;
|
|
public final static int REG_EXCMASKA1 = 0x015;
|
|
public final static int REG_EXCMASKA2 = 0x016;
|
|
public final static int REG_EXCMASKB0 = 0x018;
|
|
public final static int REG_EXCMASKB1 = 0x019;
|
|
public final static int REG_EXCMASKB2 = 0x01A;
|
|
public final static int REG_EXCBINDX0 = 0x01C;
|
|
public final static int REG_EXCBINDX1 = 0x01D;
|
|
public final static int REG_EXCBINDY0 = 0x01E;
|
|
public final static int REG_EXCBINDY1 = 0x01F;
|
|
public final static int REG_GPIOCTRL0 = 0x020;
|
|
public final static int REG_GPIOCTRL1 = 0x021;
|
|
public final static int REG_GPIOCTRL2 = 0x022;
|
|
public final static int REG_GPIOCTRL3 = 0x023;
|
|
public final static int REG_GPIOCTRL4 = 0x024;
|
|
public final static int REG_GPIOCTRL5 = 0x025;
|
|
public final static int REG_GPIOPOLARITY = 0x026;
|
|
public final static int REG_GPIOCTRL = 0x028;
|
|
public final static int REG_DPUCON = 0x02A;
|
|
public final static int REG_DPUSTAT = 0x02C;
|
|
public final static int REG_FREQCTRL = 0x02E;
|
|
public final static int REG_FREQTUNE = 0x02F;
|
|
public final static int REG_TXPOWER = 0x030;
|
|
public final static int REG_TXCTRL = 0x031;
|
|
public final static int REG_FSMSTAT0 = 0x032;
|
|
public final static int REG_FSMSTAT1 = 0x033;
|
|
public final static int REG_FIFOPCTRL = 0x034;
|
|
public final static int REG_FSMCTRL = 0x035;
|
|
public final static int REG_CCACTRL0 = 0x036;
|
|
public final static int REG_CCACTRL1 = 0x037;
|
|
public final static int REG_RSSI = 0x038;
|
|
public final static int REG_RSSISTAT = 0x039;
|
|
public final static int REG_TXFIFO_BUF = 0x03A;
|
|
public final static int REG_RXFIRST = 0x03C;
|
|
public final static int REG_RXFIFOCNT = 0x03E;
|
|
public final static int REG_TXFIFOCNT = 0x03F;
|
|
|
|
// SREG definitions (BSET/BCLR unsupported)
|
|
public final static int REG_CHIPID = 0x040;
|
|
public final static int REG_VERSION = 0x042;
|
|
public final static int REG_EXTCLOCK = 0x044;
|
|
public final static int REG_MDMCTRL0 = 0x046;
|
|
public final static int REG_MDMCTRL1 = 0x047;
|
|
public final static int REG_FREQEST = 0x048;
|
|
public final static int REG_RXCTRL = 0x04A;
|
|
public final static int REG_FSCTRL = 0x04C;
|
|
public final static int REG_FSCAL0 = 0x04E;
|
|
public final static int REG_FSCAL1 = 0x04F;
|
|
public final static int REG_FSCAL2 = 0x050;
|
|
public final static int REG_FSCAL3 = 0x051;
|
|
public final static int REG_AGCCTRL0 = 0x052;
|
|
public final static int REG_AGCCTRL1 = 0x053;
|
|
public final static int REG_AGCCTRL2 = 0x054;
|
|
public final static int REG_AGCCTRL3 = 0x055;
|
|
public final static int REG_ADCTEST0 = 0x056;
|
|
public final static int REG_ADCTEST1 = 0x057;
|
|
public final static int REG_ADCTEST2 = 0x058;
|
|
public final static int REG_MDMTEST0 = 0x05A;
|
|
public final static int REG_MDMTEST1 = 0x05B;
|
|
public final static int REG_DACTEST0 = 0x05C;
|
|
public final static int REG_DACTEST1 = 0x05D;
|
|
public final static int REG_ATEST = 0x05E;
|
|
public final static int REG_DACTEST2 = 0x05F;
|
|
public final static int REG_PTEST0 = 0x060;
|
|
public final static int REG_PTEST1 = 0x061;
|
|
public final static int REG_RESERVED = 0x062;
|
|
public final static int REG_DPUBIST = 0x07A;
|
|
public final static int REG_ACTBIST = 0x07C;
|
|
public final static int REG_RAMBIST = 0x07E;
|
|
|
|
// Instructions
|
|
public final static int INS_SNOP = 0x00;
|
|
public final static int INS_IBUFLD = 0x02;
|
|
public final static int INS_SIBUFEX = 0x03;
|
|
public final static int INS_SSAMPLECCA = 0x04;
|
|
public final static int INS_SRES = 0x0F;
|
|
public final static int INS_MEMRD = 0x10;
|
|
public final static int INS_MEMWR = 0x20;
|
|
public final static int INS_RXBUF = 0x30;
|
|
public final static int INS_RXBUFCP = 0x38;
|
|
public final static int INS_RXBUFMOV = 0x32;
|
|
public final static int INS_TXBUF = 0x3A;
|
|
public final static int INS_TXBUFCP = 0x3E;
|
|
public final static int INS_RANDOM = 0x3C;
|
|
public final static int INS_SXOSCON = 0x40;
|
|
public final static int INS_STXCAL = 0x41;
|
|
public final static int INS_SRXON = 0x42;
|
|
public final static int INS_STXON = 0x43;
|
|
public final static int INS_STXONCCA = 0x44;
|
|
public final static int INS_SRFOFF = 0x45;
|
|
public final static int INS_SXOSCOFF = 0x46;
|
|
public final static int INS_SFLUSHRX = 0x47;
|
|
public final static int INS_SFLUSHTX = 0x48;
|
|
public final static int INS_SACK = 0x49;
|
|
public final static int INS_SACKPEND = 0x4A;
|
|
public final static int INS_SNACK = 0x4B;
|
|
public final static int INS_SRXMASKBITSET = 0x4C;
|
|
public final static int INS_SRXMASKBITCLR = 0x4D;
|
|
public final static int INS_RXMASKAND = 0x4E;
|
|
public final static int INS_RXMASKOR = 0x4F;
|
|
public final static int INS_MEMCP = 0x50;
|
|
public final static int INS_MEMCPR = 0x52;
|
|
public final static int INS_MEMXCP = 0x54;
|
|
public final static int INS_MEMXWR = 0x56;
|
|
public final static int INS_BCLR = 0x58;
|
|
public final static int INS_BSET = 0x59;
|
|
public final static int INS_CTR = 0x60;
|
|
public final static int INS_CBCMAC = 0x64;
|
|
public final static int INS_UCBCMAC = 0x66;
|
|
public final static int INS_CCM = 0x68;
|
|
public final static int INS_UCCM = 0x6A;
|
|
public final static int INS_ECB = 0x70;
|
|
public final static int INS_ECBO = 0x72;
|
|
public final static int INS_ECBX = 0x74;
|
|
public final static int INS_ECBXO = 0x76;
|
|
public final static int INS_INC = 0x78;
|
|
public final static int INS_ABORT = 0x7F;
|
|
public final static int INS_REGRD = 0x80;
|
|
public final static int INS_REGWR = 0xC0;
|
|
|
|
// Status register flags
|
|
public static final int STATUS_XOSC16M_STABLE = 1 << 7;
|
|
public static final int STATUS_RSSI_VALID = 1 << 6;
|
|
public static final int STATUS_EXCEPTION_CHA = 1 << 5;
|
|
public static final int STATUS_EXCEPTION_CHB = 1 << 4;
|
|
public static final int STATUS_DPU_H = 1 << 3;
|
|
public static final int STATUS_DPU_L = 1 << 2;
|
|
public static final int STATUS_TX_ACTIVE = 1 << 1;
|
|
public static final int STATUS_RX_ACTIVE = 1 << 0;
|
|
|
|
// Exceptions (bits in the EXCFLAGx memory)
|
|
public final static int EXC_RF_IDLE = 1 << 0;
|
|
public final static int EXC_TX_FRM_DONE = 1 << 1;
|
|
public final static int EXC_RX_FRM_ABORTED = 0x20;
|
|
public final static int EXC_RX_FRM_UNDERFLOW = 0x20;
|
|
|
|
// RAM Addresses
|
|
public static final int RAM_TXFIFO = 0x100;
|
|
public static final int RAM_RXFIFO = 0x180;
|
|
public static final int RAM_IEEEADDR = 0x3EA;
|
|
public static final int RAM_PANID = 0x3F2;
|
|
public static final int RAM_SHORTADDR = 0x3F4;
|
|
// public static final int RAM_KEY0 = 0x100;
|
|
// public static final int RAM_RXNONCE = 0x110;
|
|
// public static final int RAM_SABUF = 0x120;
|
|
// public static final int RAM_KEY1 = 0x130;
|
|
// public static final int RAM_TXNONCE = 0x140;
|
|
// public static final int RAM_CBCSTATE = 0x150;
|
|
|
|
/* one single byte instruction can be stored in the IBUF */
|
|
int instructionBuffer = 0;
|
|
|
|
// IOCFG0 memory Bit masks
|
|
public static final int BCN_ACCEPT = (1<<11);
|
|
public static final int FIFOP_THR = 0x7F;
|
|
|
|
// IOCFG1 memory Bit Masks
|
|
// public static final int SFDMUX = 0x3E0;
|
|
// public static final int CCAMUX = 0x1F;
|
|
|
|
// CCAMUX values
|
|
// public static final int CCAMUX_CCA = 0;
|
|
// public static final int CCAMUX_XOSC16M_STABLE = 24;
|
|
|
|
// FRMFILT0/FRMCTRL0 values
|
|
public static final int FRAME_FILTER = (1 << 0);
|
|
public static final int AUTOCRC = (1 << 6);
|
|
public static final int AUTOACK = (1 << 5);
|
|
|
|
// FRMFILT1
|
|
public static final int ACCEPT_FT_4TO7_RESERVED = (1 << 7);
|
|
public static final int ACCEPT_FT_3_MAC_CMD = (1 << 6);
|
|
public static final int ACCEPT_FT_2_ACK = (1 << 5);
|
|
public static final int ACCEPT_FT_1_DATA = (1 << 4);
|
|
public static final int ACCEPT_FT_0_BEACON = (1 << 3);
|
|
|
|
public static final int SHORT_ADDRESS = 2;
|
|
public static final int LONG_ADDRESS = 3;
|
|
|
|
|
|
// The Operation modes of the CC2520
|
|
public static final int MODE_TXRX_OFF = 0x00;
|
|
public static final int MODE_RX_ON = 0x01;
|
|
public static final int MODE_TXRX_ON = 0x02;
|
|
public static final int MODE_POWER_OFF = 0x03;
|
|
public static final int MODE_MAX = MODE_POWER_OFF;
|
|
private static final String[] MODE_NAMES = new String[] {
|
|
"off", "listen", "transmit", "power_off"
|
|
};
|
|
|
|
// State Machine - Datasheet Figure 30 page 85
|
|
public enum RadioState {
|
|
VREG_OFF(-2),
|
|
POWER_DOWN(-1),
|
|
IDLE(0),
|
|
RX_CALIBRATE(2),
|
|
RX_SFD_SEARCH(3),
|
|
RX_WAIT(14),
|
|
RX_FRAME(15),
|
|
RX_OVERFLOW(17),
|
|
TX_CALIBRATE(32),
|
|
TX_PREAMBLE(34),
|
|
TX_FRAME(37),
|
|
TX_ACK_CALIBRATE(48),
|
|
TX_ACK_PREAMBLE(49),
|
|
TX_ACK(52),
|
|
TX_UNDERFLOW(56);
|
|
|
|
private final int state;
|
|
RadioState(int stateNo) {
|
|
state = stateNo;
|
|
}
|
|
|
|
public int getFSMState() {
|
|
return state;
|
|
}
|
|
};
|
|
|
|
// FCF High
|
|
public static final int FRAME_TYPE = 0x07;
|
|
public static final int SECURITY_ENABLED = (1 << 3);
|
|
public static final int FRAME_PENDING = (1 << 4);
|
|
public static final int ACK_REQUEST = (1 << 5);
|
|
public static final int INTRA_PAN = (1 << 6);
|
|
|
|
public static final int TYPE_BEACON_FRAME = 0x00;
|
|
public static final int TYPE_DATA_FRAME = 0x01;
|
|
public static final int TYPE_ACK_FRAME = 0x02;
|
|
|
|
// FCF Low
|
|
public static final int DESTINATION_ADDRESS_MODE = 0x30;
|
|
public static final int SOURCE_ADDRESS_MODE = 0x3;
|
|
|
|
// Position of SEQ-NO in ACK packet...
|
|
public static final int ACK_SEQPOS = 3;
|
|
|
|
private RadioState stateMachine = RadioState.VREG_OFF;
|
|
|
|
// 802.15.4 symbol period in ms
|
|
public static final double SYMBOL_PERIOD = 0.016; // 16 us
|
|
|
|
private static final int[] BC_ADDRESS = new int[] {0xff, 0xff};
|
|
|
|
private int shrPos;
|
|
private int txfifoPos;
|
|
private boolean txfifoFlush; // TXFIFO is automatically flushed on next write
|
|
private int rxfifoReadLeft; // number of bytes left to read from current packet
|
|
private int rxlen;
|
|
private int rxread;
|
|
private int zeroSymbols;
|
|
|
|
/* RSSI is an externally set value of the RSSI for this CC2520 */
|
|
/* low RSSI => CCA = true in normal mode */
|
|
|
|
private int rssi = -100;
|
|
private static int RSSI_OFFSET = -45; /* cc2520 datasheet */
|
|
|
|
/* This is the magical LQI */
|
|
private int corrval = 37;
|
|
|
|
/* FIFOP Threshold */
|
|
private int fifopThr = 0x40;
|
|
|
|
/* Configuration for frame filtering and auto acknowledgments */
|
|
private boolean frameFilter = false;
|
|
private boolean autoAck = false;
|
|
private boolean shouldAck = false;
|
|
private boolean ackRequest = false;
|
|
private boolean autoCRC = false;
|
|
|
|
// Data from last received packet
|
|
private int dsn = 0;
|
|
private int fcf0 = 0;
|
|
private int fcf1 = 0;
|
|
private int frameType = 0;
|
|
private boolean crcOk = false;
|
|
|
|
private int activeFrequency = 0;
|
|
private int activeChannel = 0;
|
|
|
|
//private int status = STATUS_XOSC16M_STABLE | STATUS_RSSI_VALID;
|
|
private int status = 0;
|
|
|
|
private final int[] memory = new int[0x400]; /* total memory */
|
|
|
|
private CC2520SPI cc2520SPI = new CC2520SPI(this);
|
|
private SPICommand command;
|
|
private int[] spiData = new int[20]; /* SPI data buffer */
|
|
private int spiLen;
|
|
|
|
// Buffer to hold 5 byte Synchronization header, as it is not written to the TXFIFO
|
|
private final byte[] SHR = new byte[5];
|
|
|
|
/* the data that should be SPI response */
|
|
private int outputSPI;
|
|
|
|
private boolean chipSelect;
|
|
private final GPIO[] gpio = new GPIO[6];
|
|
private GPIO ccaGPIO;
|
|
private GPIO fifopGPIO;
|
|
private GPIO fifoGPIO;
|
|
private GPIO sfdGPIO;
|
|
private boolean currentFIFO;
|
|
private boolean currentFIFOP;
|
|
|
|
/* current CCA value */
|
|
private boolean currentCCA = false;
|
|
|
|
private int txCursor;
|
|
private boolean isRadioOn;
|
|
|
|
private TimeEvent oscillatorEvent = new TimeEvent(0, "CC2520 OSC") {
|
|
public void execute(long t) {
|
|
status |= STATUS_XOSC16M_STABLE;
|
|
if(DEBUG) log("Oscillator Stable Event.");
|
|
setState(RadioState.IDLE);
|
|
updateCCA();
|
|
}
|
|
};
|
|
|
|
private TimeEvent vregEvent = new TimeEvent(0, "CC2520 VREG") {
|
|
public void execute(long t) {
|
|
if(DEBUG) log("VREG Started at: " + t + " cyc: " +
|
|
cpu.cycles + " " + getTime());
|
|
isRadioOn = true;
|
|
setState(RadioState.POWER_DOWN);
|
|
updateCCA();
|
|
}
|
|
};
|
|
|
|
private TimeEvent sendEvent = new TimeEvent(0, "CC2520 Send") {
|
|
public void execute(long t) {
|
|
txNext();
|
|
}
|
|
};
|
|
|
|
private TimeEvent ackEvent = new TimeEvent(0, "CC2520 Ack") {
|
|
public void execute(long t) {
|
|
ackNext();
|
|
}
|
|
};
|
|
|
|
private TimeEvent shrEvent = new TimeEvent(0, "CC2520 SHR") {
|
|
public void execute(long t) {
|
|
shrNext();
|
|
}
|
|
};
|
|
|
|
private TimeEvent symbolEvent = new TimeEvent(0, "CC2520 Symbol") {
|
|
public void execute(long t) {
|
|
switch(stateMachine) {
|
|
case RX_CALIBRATE:
|
|
setState(RadioState.RX_SFD_SEARCH);
|
|
break;
|
|
/* this will be called 8 symbols after first SFD_SEARCH */
|
|
case RX_SFD_SEARCH:
|
|
status |= STATUS_RSSI_VALID;
|
|
memory[REG_RSSISTAT] = 1;
|
|
updateCCA();
|
|
break;
|
|
|
|
case TX_CALIBRATE:
|
|
setState(RadioState.TX_PREAMBLE);
|
|
break;
|
|
|
|
case RX_WAIT:
|
|
setState(RadioState.RX_SFD_SEARCH);
|
|
break;
|
|
|
|
case TX_ACK_CALIBRATE:
|
|
setState(RadioState.TX_ACK_PREAMBLE);
|
|
break;
|
|
|
|
default:
|
|
// Ignore other states
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
private boolean overflow = false;
|
|
private boolean frameRejected = false;
|
|
|
|
private int ackPos;
|
|
/* type = 2 (ACK), third byte needs to be sequence number... */
|
|
private int[] ackBuf = {0x05, 0x02, 0x00, 0x00, 0x00, 0x00};
|
|
private boolean ackFramePending = false;
|
|
private CCITT_CRC rxCrc = new CCITT_CRC();
|
|
private CCITT_CRC txCrc = new CCITT_CRC();
|
|
|
|
private final ArrayFIFO rxFIFO = new ArrayFIFO("RXFIFO", memory, 128, 128);
|
|
|
|
public CC2520(MSP430Core cpu) {
|
|
super("CC2520", "Radio", cpu);
|
|
|
|
for (int i = 0; i < gpio.length; i++) {
|
|
gpio[i] = new GPIO();
|
|
}
|
|
|
|
setModeNames(MODE_NAMES);
|
|
setMode(MODE_POWER_OFF);
|
|
rxFIFO.reset();
|
|
overflow = false;
|
|
reset();
|
|
}
|
|
|
|
public RadioState getState() {
|
|
return stateMachine;
|
|
}
|
|
|
|
private int getFCFReservedMask() {
|
|
return (memory[REG_FRMFILT0] >> 4) & 7;
|
|
}
|
|
|
|
private int getFCFMaxFrameVersion() {
|
|
return (memory[REG_FRMFILT0] >> 2) & 3;
|
|
}
|
|
|
|
private void updateGPIOConfig() {
|
|
int bit = 1;
|
|
for (GPIO io : gpio) {
|
|
io.setPolarity((memory[REG_GPIOPOLARITY] & bit) > 0);
|
|
bit = bit << 1;
|
|
}
|
|
}
|
|
|
|
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;
|
|
frameFilter = true;
|
|
memory[REG_FRMFILT1] = 0x78;
|
|
// autocrc enabled, autoack disabled
|
|
memory[REG_FRMCTRL0] = 0x40;
|
|
autoCRC = true;
|
|
autoAck = false;
|
|
|
|
memory[REG_MDMCTRL0] = 0x45;
|
|
memory[REG_MDMCTRL1] = 0x3e;
|
|
memory[REG_FSMSTAT0] = 0;
|
|
memory[REG_FSMSTAT1] = 0;
|
|
memory[REG_RSSISTAT] = 0;
|
|
memory[REG_TXPOWER] = 0x06;
|
|
memory[REG_FIFOPCTRL] = fifopThr = 0x40;
|
|
memory[REG_FREQCTRL] = 0x0b;
|
|
|
|
/* back to default configuration of GPIOs */
|
|
memory[REG_GPIOPOLARITY] = 0x3f;
|
|
updateGPIOConfig();
|
|
|
|
fifoGPIO = gpio[1];
|
|
fifopGPIO = gpio[2];
|
|
ccaGPIO = gpio[3];
|
|
sfdGPIO = gpio[4];
|
|
|
|
setFIFO(false);
|
|
setFIFOP(false);
|
|
setSFD(false);
|
|
updateCCA();
|
|
}
|
|
|
|
private boolean setState(RadioState state) {
|
|
if(DEBUG) log("State transition from " + stateMachine + " to " + state);
|
|
stateMachine = state;
|
|
/* write to FSM state register */
|
|
memory[REG_FSMSTAT0] = (memory[REG_FSMSTAT0] & 0x3f);//state.getFSMState();
|
|
|
|
switch(stateMachine) {
|
|
|
|
case VREG_OFF:
|
|
if (DEBUG) log("VREG Off.");
|
|
flushRX();
|
|
flushTX();
|
|
status &= ~(STATUS_RSSI_VALID | STATUS_XOSC16M_STABLE);
|
|
memory[REG_RSSISTAT] = 0;
|
|
crcOk = false;
|
|
reset();
|
|
setMode(MODE_POWER_OFF);
|
|
updateCCA();
|
|
break;
|
|
|
|
case POWER_DOWN:
|
|
rxFIFO.reset();
|
|
status &= ~(STATUS_RSSI_VALID | STATUS_XOSC16M_STABLE);
|
|
memory[REG_RSSISTAT] = 0;
|
|
crcOk = false;
|
|
reset();
|
|
setMode(MODE_POWER_OFF);
|
|
updateCCA();
|
|
break;
|
|
|
|
case RX_CALIBRATE:
|
|
/* should be 12 according to specification */
|
|
setSymbolEvent(12);
|
|
setMode(MODE_RX_ON);
|
|
break;
|
|
case RX_SFD_SEARCH:
|
|
zeroSymbols = 0;
|
|
/* eight symbols after first SFD search RSSI will be valid */
|
|
if ((status & STATUS_RSSI_VALID) == 0) {
|
|
setSymbolEvent(8);
|
|
}
|
|
// status |= STATUS_RSSI_VALID;
|
|
updateCCA();
|
|
setMode(MODE_RX_ON);
|
|
break;
|
|
|
|
case TX_CALIBRATE:
|
|
/* 12 symbols calibration, and one byte's wait since we deliver immediately
|
|
* to listener when after calibration?
|
|
*/
|
|
setSymbolEvent(12 + 2);
|
|
setMode(MODE_TXRX_ON);
|
|
break;
|
|
|
|
case TX_PREAMBLE:
|
|
shrPos = 0;
|
|
SHR[0] = 0;
|
|
SHR[1] = 0;
|
|
SHR[2] = 0;
|
|
SHR[3] = 0;
|
|
SHR[4] = 0x7A;
|
|
shrNext();
|
|
break;
|
|
|
|
case TX_FRAME:
|
|
txfifoPos = 0;
|
|
// Reset CRC ok flag to disable software acknowledgments until next received packet
|
|
crcOk = false;
|
|
txNext();
|
|
break;
|
|
|
|
case RX_WAIT:
|
|
setSymbolEvent(8);
|
|
setMode(MODE_RX_ON);
|
|
break;
|
|
|
|
case IDLE:
|
|
status &= ~STATUS_RSSI_VALID;
|
|
memory[REG_RSSISTAT] = 0;
|
|
setMode(MODE_TXRX_OFF);
|
|
updateCCA();
|
|
break;
|
|
|
|
case TX_ACK_CALIBRATE:
|
|
/* TX active during ACK + NOTE: we ignore the SFD when receiving full packets so
|
|
* we need to add another extra 2 symbols here to get a correct timing */
|
|
status |= STATUS_TX_ACTIVE;
|
|
memory[REG_FSMSTAT1] |= (1 << 1);
|
|
setSymbolEvent(12 + 2 + 2);
|
|
setMode(MODE_TXRX_ON);
|
|
break;
|
|
case TX_ACK_PREAMBLE:
|
|
/* same as normal preamble ?? */
|
|
shrPos = 0;
|
|
SHR[0] = 0;
|
|
SHR[1] = 0;
|
|
SHR[2] = 0;
|
|
SHR[3] = 0;
|
|
SHR[4] = 0x7A;
|
|
shrNext();
|
|
break;
|
|
case TX_ACK:
|
|
ackPos = 0;
|
|
// Reset CRC ok flag to disable software acknowledgments until next received packet
|
|
crcOk = false;
|
|
ackNext();
|
|
break;
|
|
case RX_FRAME:
|
|
/* mark position of frame start - for rejecting when address is wrong */
|
|
rxFIFO.mark();
|
|
rxread = 0;
|
|
frameRejected = false;
|
|
shouldAck = false;
|
|
crcOk = false;
|
|
break;
|
|
|
|
case RX_OVERFLOW:
|
|
break;
|
|
|
|
case TX_UNDERFLOW:
|
|
// TODO handle TX underflow
|
|
break;
|
|
}
|
|
|
|
/* Notify state listener */
|
|
stateChanged(stateMachine.state);
|
|
|
|
return true;
|
|
}
|
|
|
|
private void rejectFrame() {
|
|
// Immediately jump to SFD Search again... something more???
|
|
/* reset state */
|
|
rxFIFO.restore();
|
|
setSFD(false);
|
|
setFIFO(rxFIFO.length() > 0);
|
|
frameRejected = true;
|
|
}
|
|
|
|
/* variables for the address recognition */
|
|
int destinationAddressMode = 0;
|
|
boolean decodeAddress = false;
|
|
/* Receive a byte from the radio medium
|
|
* @see se.sics.mspsim.chip.RFListener#receivedByte(byte)
|
|
*/
|
|
public void receivedByte(byte data) {
|
|
// Received a byte from the "air"
|
|
|
|
if (DEBUG)
|
|
log("RF Byte received: " + Utils.hex8(data) + " state: " + stateMachine + " noZeroes: " + zeroSymbols +
|
|
((stateMachine == RadioState.RX_SFD_SEARCH || stateMachine == RadioState.RX_FRAME) ? "" : " *** Ignored"));
|
|
|
|
if(stateMachine == RadioState.RX_SFD_SEARCH) {
|
|
// Look for the preamble (4 zero bytes) followed by the SFD byte 0x7A
|
|
if(data == 0) {
|
|
// Count zero bytes
|
|
zeroSymbols++;
|
|
} else if(zeroSymbols >= 4 && data == 0x7A) {
|
|
// If the received byte is !zero, we have counted 4 zero bytes prior to this one,
|
|
// and the current received byte == 0x7A (SFD), we're in sync.
|
|
// In RX mode, SFD goes high when the SFD is received
|
|
setSFD(true);
|
|
if (DEBUG) log("RX: Preamble/SFD Synchronized.");
|
|
setState(RadioState.RX_FRAME);
|
|
} else {
|
|
/* if not four zeros and 0x7A then no zeroes... */
|
|
zeroSymbols = 0;
|
|
}
|
|
|
|
} else if(stateMachine == RadioState.RX_FRAME) {
|
|
if (overflow) {
|
|
/* if the CC2520 RX FIFO is in overflow - it needs a flush before receiving again */
|
|
} else if(rxFIFO.isFull()) {
|
|
setRxOverflow();
|
|
} else {
|
|
if (!frameRejected) {
|
|
rxFIFO.write(data);
|
|
if (rxread == 0) {
|
|
rxCrc.setCRC(0);
|
|
rxlen = data & 0xff;
|
|
//System.out.println("Starting to get packet at: " + rxfifoWritePos + " len = " + rxlen);
|
|
decodeAddress = frameFilter;
|
|
if (DEBUG) log("RX: Start frame length " + rxlen);
|
|
// FIFO pin goes high after length byte is written to RXFIFO
|
|
setFIFO(true);
|
|
} else if (rxread < rxlen - 1) {
|
|
/* As long as we are not in the length or FCF (CRC) we count CRC */
|
|
rxCrc.addBitrev(data & 0xff);
|
|
if (rxread == 1) {
|
|
fcf0 = data & 0xff;
|
|
frameType = fcf0 & FRAME_TYPE;
|
|
} else if (rxread == 2) {
|
|
fcf1 = data & 0xff;
|
|
|
|
if (frameFilter
|
|
&& (((getFCFReservedMask() & (((fcf0 & 3) << 1) | (fcf1 & 1))) != 0)
|
|
|| (getFCFMaxFrameVersion() < ((fcf0 >> 2) & 3)))) {
|
|
// Illegal frame version or reserved bits set
|
|
rejectFrame();
|
|
} else if (frameType == TYPE_DATA_FRAME) {
|
|
ackRequest = (fcf0 & ACK_REQUEST) > 0;
|
|
destinationAddressMode = (fcf1 >> 2) & 3;
|
|
/* check this !!! */
|
|
if (frameFilter) {
|
|
if ((destinationAddressMode != LONG_ADDRESS
|
|
&& destinationAddressMode != SHORT_ADDRESS)
|
|
|| (memory[REG_FRMFILT1] & ACCEPT_FT_1_DATA) == 0) {
|
|
rejectFrame();
|
|
}
|
|
}
|
|
} else if (frameType == TYPE_ACK_FRAME) {
|
|
decodeAddress = false;
|
|
ackRequest = false;
|
|
if (frameFilter) {
|
|
if (rxlen != 5
|
|
|| (memory[REG_FRMFILT1] & ACCEPT_FT_2_ACK) == 0) {
|
|
rejectFrame();
|
|
}
|
|
}
|
|
} else if (frameType == TYPE_BEACON_FRAME) {
|
|
decodeAddress = false;
|
|
ackRequest = false;
|
|
destinationAddressMode = (fcf1 >> 2) & 3;
|
|
if (frameFilter) {
|
|
if (rxlen < 9
|
|
|| (memory[REG_FRMFILT1] & ACCEPT_FT_0_BEACON) == 0
|
|
|| destinationAddressMode != 0) {
|
|
rejectFrame();
|
|
}
|
|
}
|
|
} else if (frameFilter) {
|
|
/* illegal frame when decoding address... */
|
|
rejectFrame();
|
|
}
|
|
} else if (rxread == 3) {
|
|
// save data sequence number
|
|
dsn = data & 0xff;
|
|
} else if (decodeAddress) {
|
|
boolean flushPacket = false;
|
|
/* here we decode the address !!! */
|
|
if (destinationAddressMode == LONG_ADDRESS && rxread == 8 + 5) {
|
|
/* here we need to check that this address is correct compared to the stored address */
|
|
flushPacket = !rxFIFO.tailEquals(memory, RAM_IEEEADDR, 8);
|
|
flushPacket |= !rxFIFO.tailEquals(memory, RAM_PANID, 2, 8)
|
|
&& !rxFIFO.tailEquals(BC_ADDRESS, 0, 2, 8);
|
|
decodeAddress = false;
|
|
} else if (destinationAddressMode == SHORT_ADDRESS && rxread == 2 + 5){
|
|
/* should check short address */
|
|
flushPacket = !rxFIFO.tailEquals(BC_ADDRESS, 0, 2)
|
|
&& !rxFIFO.tailEquals(memory, RAM_SHORTADDR, 2);
|
|
flushPacket |= !rxFIFO.tailEquals(memory, RAM_PANID, 2, 2)
|
|
&& !rxFIFO.tailEquals(BC_ADDRESS, 0, 2, 2);
|
|
decodeAddress = false;
|
|
}
|
|
if (flushPacket) {
|
|
rejectFrame();
|
|
}
|
|
}
|
|
}
|
|
|
|
/* In RX mode, FIFOP goes high when the size of the first enqueued packet exceeds
|
|
* the programmable threshold and address recognition isn't ongoing */
|
|
if (currentFIFOP == false
|
|
&& rxFIFO.length() <= rxlen + 1
|
|
&& !decodeAddress && !frameRejected
|
|
&& rxFIFO.length() > fifopThr) {
|
|
setFIFOP(true);
|
|
if (DEBUG) log("RX: FIFOP Threshold reached - setting FIFOP");
|
|
}
|
|
}
|
|
|
|
if (rxread++ == rxlen) {
|
|
if (frameRejected) {
|
|
if (DEBUG) log("Frame rejected - setting SFD to false and RXWAIT\n");
|
|
setSFD(false);
|
|
setState(RadioState.RX_WAIT);
|
|
return;
|
|
}
|
|
// In RX mode, FIFOP goes high, if threshold is higher than frame length....
|
|
|
|
// Here we check the CRC of the packet!
|
|
//System.out.println("Reading from " + ((rxfifoWritePos + 128 - 2) & 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()));
|
|
}
|
|
// 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;
|
|
}
|
|
|
|
/* 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 */
|
|
if (rxFIFO.length() <= rxlen + 1) {
|
|
setFIFOP(true);
|
|
} else {
|
|
if (DEBUG) log("Did not set FIFOP rxfifoLen: " + rxFIFO.length() + " rxlen: " + rxlen);
|
|
}
|
|
setSFD(false);
|
|
if (DEBUG) log("RX: Complete: packetStart: " + rxFIFO.stateToString());
|
|
|
|
/* if either manual ack request (shouldAck) or autoack + ACK_REQ on package do ack! */
|
|
/* Autoack-mode + good CRC => autoack */
|
|
if (((autoAck && ackRequest) || shouldAck) && crcOk) {
|
|
setState(RadioState.TX_ACK_CALIBRATE);
|
|
} else {
|
|
setState(RadioState.RX_WAIT);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* API used in CC2520 SPI for both memory and registers */
|
|
void writeMemory(int address, int data) {
|
|
// System.out.printf("CC2520: writing to %x => %x\n", address, data);
|
|
int oldValue = memory[address];
|
|
memory[address] = data;
|
|
switch(address) {
|
|
case REG_FRMFILT0:
|
|
frameFilter = (data & FRAME_FILTER) != 0;
|
|
break;
|
|
case REG_FRMCTRL0:
|
|
autoCRC = (data & AUTOCRC) != 0;
|
|
autoAck = (data & AUTOACK) != 0;
|
|
break;
|
|
case REG_TXPOWER:
|
|
if (!isDefinedTxPower(data)) {
|
|
logw(WarningType.EXECUTION, "*** Warning - writing an undefined TXPOWER value (0x"
|
|
+ Utils.hex8(data) + ") to CC2520!!!");
|
|
}
|
|
break;
|
|
case REG_FIFOPCTRL:
|
|
fifopThr = data & FIFOP_THR;
|
|
if (DEBUG) log("FIFOPCTRL: 0x" + Utils.hex16(oldValue) + " => 0x" + Utils.hex16(data));
|
|
break;
|
|
case REG_GPIOPOLARITY:
|
|
if (DEBUG) log("GIOPOLARITY: 0x" + Utils.hex16(oldValue) + " => 0x" + Utils.hex16(data));
|
|
if (oldValue != data) {
|
|
updateGPIOConfig();
|
|
}
|
|
break;
|
|
// case REG_IOCFG1:
|
|
// if (DEBUG)
|
|
// log("IOCFG1: SFDMUX "
|
|
// + ((memory[address] & SFDMUX) >> SFDMUX)
|
|
// + " 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) {
|
|
int oldChannel = activeChannel;
|
|
updateActiveFrequency();
|
|
if (oldChannel != activeChannel) {
|
|
listener.channelChanged(activeChannel);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
configurationChanged(address, oldValue, data);
|
|
}
|
|
|
|
int readMemory(int address) {
|
|
switch(address) {
|
|
case REG_RXFIFOCNT:
|
|
return rxFIFO.length();
|
|
}
|
|
return memory[address];
|
|
}
|
|
|
|
@Override
|
|
public void dataReceived(USARTSource source, int data) {
|
|
outputSPI = status; /* if nothing replace the outputSPI it will be output */
|
|
if (DEBUG) {
|
|
log("byte received: " + Utils.hex8(data) +
|
|
" (" + ((data >= ' ' && data <= 'Z') ? (char) data : '.') + ')' +
|
|
" CS: " + chipSelect + " SPI(" + spiLen + "): " + (command == null ? "<waiting>" : command.name)
|
|
+ " State: " + stateMachine);
|
|
}
|
|
|
|
if (!chipSelect) {
|
|
// Chip is not selected
|
|
return;
|
|
}
|
|
|
|
if (stateMachine == RadioState.VREG_OFF) {
|
|
/* No VREG but chip select */
|
|
source.byteReceived(0);
|
|
logw(WarningType.EXECUTION, "**** Warning - writing to CC2520 when VREG is off!!!");
|
|
return;
|
|
}
|
|
|
|
if (command == null) {
|
|
command = cc2520SPI.getCommand(data);
|
|
if (command == null) {
|
|
logw(WarningType.EMULATION_ERROR, "**** Warning - not implemented command on SPI: " + data);
|
|
} else if (DEBUG) {
|
|
if (!"SNOP".equals(command.name)) {
|
|
log("SPI command: " + command.name);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* command handling */
|
|
spiData[spiLen] = data;
|
|
/* ensure that we do not store too many SPI data items */
|
|
if (spiLen < (spiData.length - 1)) {
|
|
spiLen++;
|
|
}
|
|
|
|
if (command != null) {
|
|
command.dataReceived(data);
|
|
if (spiLen == command.commandLen) {
|
|
// System.out.println("CC2520 Executing command: " + command.name);
|
|
command.executeSPICommand();
|
|
command = null;
|
|
spiLen = 0;
|
|
}
|
|
}
|
|
source.byteReceived(outputSPI);
|
|
}
|
|
|
|
void rxon() {
|
|
if(stateMachine == RadioState.IDLE) {
|
|
setState(RadioState.RX_CALIBRATE);
|
|
//updateActiveFrequency();
|
|
if (DEBUG) {
|
|
log("Strobe RX-ON!!!");
|
|
}
|
|
} else {
|
|
if (DEBUG) log("WARNING: SRXON when not IDLE");
|
|
}
|
|
}
|
|
|
|
void rxtxoff() {
|
|
if (DEBUG) {
|
|
log("Strobe RXTX-OFF!!! at " + cpu.cycles);
|
|
if (stateMachine == RadioState.TX_ACK ||
|
|
stateMachine == RadioState.TX_FRAME ||
|
|
stateMachine == RadioState.RX_FRAME) {
|
|
log("WARNING: turning off RXTX during " + stateMachine);
|
|
}
|
|
}
|
|
setState(RadioState.IDLE);
|
|
}
|
|
|
|
void stxon() {
|
|
// State transition valid from IDLE state or all RX states
|
|
if( (stateMachine == RadioState.IDLE) ||
|
|
(stateMachine == RadioState.RX_CALIBRATE) ||
|
|
(stateMachine == RadioState.RX_SFD_SEARCH) ||
|
|
(stateMachine == RadioState.RX_FRAME) ||
|
|
(stateMachine == RadioState.RX_OVERFLOW) ||
|
|
(stateMachine == RadioState.RX_WAIT)) {
|
|
status |= STATUS_TX_ACTIVE;
|
|
memory[REG_FSMSTAT1] |= (1 << 1);
|
|
setState(RadioState.TX_CALIBRATE);
|
|
if (sendEvents) {
|
|
sendEvent("STXON", null);
|
|
}
|
|
// Starting up TX subsystem - indicate that we are in TX mode!
|
|
if (DEBUG) log("Strobe STXON - transmit on! at " + cpu.cycles);
|
|
}
|
|
}
|
|
|
|
void stxoncca() {
|
|
// Only valid from all RX states,
|
|
// since CCA requires ??(look this up) receive symbol periods to be valid
|
|
if( (stateMachine == RadioState.RX_CALIBRATE) ||
|
|
(stateMachine == RadioState.RX_SFD_SEARCH) ||
|
|
(stateMachine == RadioState.RX_FRAME) ||
|
|
(stateMachine == RadioState.RX_OVERFLOW) ||
|
|
(stateMachine == RadioState.RX_WAIT)) {
|
|
|
|
if (sendEvents) {
|
|
sendEvent("STXON_CCA", null);
|
|
}
|
|
|
|
if(currentCCA) {
|
|
status |= STATUS_TX_ACTIVE;
|
|
memory[REG_FSMSTAT1] |= (1 << 1);
|
|
setState(RadioState.TX_CALIBRATE);
|
|
if (DEBUG) log("Strobe STXONCCA - transmit on! at " + cpu.cycles);
|
|
}else{
|
|
if (DEBUG) log("STXONCCA Ignored, CCA false");
|
|
}
|
|
}
|
|
}
|
|
|
|
void sack(boolean pend) {
|
|
// Set the frame pending flag for all future autoack based on SACK/SACKPEND
|
|
ackFramePending = pend;
|
|
if (stateMachine == RadioState.RX_FRAME) {
|
|
shouldAck = true;
|
|
} else if (crcOk) {
|
|
setState(RadioState.TX_ACK_CALIBRATE);
|
|
}
|
|
}
|
|
|
|
private void shrNext() {
|
|
if(shrPos == 5) {
|
|
// Set SFD high
|
|
setSFD(true);
|
|
|
|
if (stateMachine == RadioState.TX_PREAMBLE) {
|
|
setState(RadioState.TX_FRAME);
|
|
} else if (stateMachine == RadioState.TX_ACK_PREAMBLE) {
|
|
setState(RadioState.TX_ACK);
|
|
} else {
|
|
log("Can not move to TX_FRAME or TX_ACK after preamble since radio is in wrong mode: " +
|
|
stateMachine);
|
|
}
|
|
} else {
|
|
if (rfListener != null) {
|
|
if (DEBUG) log("transmitting byte: " + Utils.hex8(SHR[shrPos]));
|
|
rfListener.receivedByte(SHR[shrPos]);
|
|
}
|
|
shrPos++;
|
|
cpu.scheduleTimeEventMillis(shrEvent, SYMBOL_PERIOD * 2);
|
|
}
|
|
}
|
|
|
|
private void txNext() {
|
|
if(txfifoPos <= memory[RAM_TXFIFO]) {
|
|
int len = memory[RAM_TXFIFO] & 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();
|
|
}
|
|
}
|
|
|
|
if (txfifoPos > 0x7f) {
|
|
logw(WarningType.EXECUTION, "**** Warning - packet size too large - repeating packet bytes txfifoPos: " + txfifoPos);
|
|
}
|
|
if (rfListener != null) {
|
|
if (DEBUG) log("transmitting byte: " + Utils.hex8(memory[RAM_TXFIFO + (txfifoPos & 0x7f)] & 0xFF));
|
|
rfListener.receivedByte((byte)(memory[RAM_TXFIFO + (txfifoPos & 0x7f)] & 0xFF));
|
|
}
|
|
txfifoPos++;
|
|
// Two symbol periods to send a byte...
|
|
cpu.scheduleTimeEventMillis(sendEvent, SYMBOL_PERIOD * 2);
|
|
} else {
|
|
if (DEBUG) log("Completed Transmission.");
|
|
status &= ~STATUS_TX_ACTIVE;
|
|
memory[REG_FSMSTAT1] &= ~(1 << 1);
|
|
setSFD(false);
|
|
if (overflow) {
|
|
/* TODO: is it going back to overflow here ?=? */
|
|
setState(RadioState.RX_OVERFLOW);
|
|
} else {
|
|
setState(RadioState.RX_CALIBRATE);
|
|
}
|
|
/* Back to RX ON */
|
|
setMode(MODE_RX_ON);
|
|
txfifoFlush = true;
|
|
}
|
|
}
|
|
|
|
private void ackNext() {
|
|
if (ackPos < ackBuf.length) {
|
|
if(ackPos == 0) {
|
|
txCrc.setCRC(0);
|
|
if (ackFramePending) {
|
|
ackBuf[1] |= FRAME_PENDING;
|
|
} else {
|
|
ackBuf[1] &= ~FRAME_PENDING;
|
|
}
|
|
// set dsn
|
|
ackBuf[3] = dsn;
|
|
int len = 4;
|
|
for (int i = 1; i < len; i++) {
|
|
txCrc.addBitrev(ackBuf[i] & 0xff);
|
|
}
|
|
ackBuf[4] = txCrc.getCRCHi();
|
|
ackBuf[5] = txCrc.getCRCLow();
|
|
}
|
|
if (rfListener != null) {
|
|
if (DEBUG) log("transmitting byte: " + Utils.hex8(memory[RAM_TXFIFO + (txfifoPos & 0x7f)] & 0xFF));
|
|
|
|
rfListener.receivedByte((byte)(ackBuf[ackPos] & 0xFF));
|
|
}
|
|
ackPos++;
|
|
// Two symbol periods to send a byte...
|
|
cpu.scheduleTimeEventMillis(ackEvent, SYMBOL_PERIOD * 2);
|
|
} else {
|
|
if (DEBUG) log("Completed Transmission of ACK.");
|
|
status &= ~STATUS_TX_ACTIVE;
|
|
memory[REG_FSMSTAT1] &= ~(1 << 1);
|
|
setSFD(false);
|
|
setState(RadioState.RX_CALIBRATE);
|
|
/* Back to RX ON */
|
|
setMode(MODE_RX_ON);
|
|
}
|
|
}
|
|
|
|
|
|
private void setSymbolEvent(int symbols) {
|
|
double period = SYMBOL_PERIOD * symbols;
|
|
cpu.scheduleTimeEventMillis(symbolEvent, period);
|
|
//log("Set Symbol event: " + period);
|
|
}
|
|
|
|
void startOscillator() {
|
|
// 1ms crystal startup from datasheet pg12
|
|
cpu.scheduleTimeEventMillis(oscillatorEvent, 1);
|
|
}
|
|
|
|
void stopOscillator() {
|
|
status &= ~STATUS_XOSC16M_STABLE;
|
|
setState(RadioState.POWER_DOWN);
|
|
if (DEBUG) log("Oscillator Off.");
|
|
// Reset state
|
|
setFIFOP(false);
|
|
}
|
|
|
|
void flushRX() {
|
|
if (DEBUG) {
|
|
log("Flushing RX len = " + rxFIFO.length());
|
|
}
|
|
rxFIFO.reset();
|
|
setSFD(false);
|
|
setFIFOP(false);
|
|
setFIFO(false);
|
|
overflow = false;
|
|
/* goto RX Calibrate */
|
|
if( (stateMachine == RadioState.RX_CALIBRATE) ||
|
|
(stateMachine == RadioState.RX_SFD_SEARCH) ||
|
|
(stateMachine == RadioState.RX_FRAME) ||
|
|
(stateMachine == RadioState.RX_OVERFLOW) ||
|
|
(stateMachine == RadioState.RX_WAIT)) {
|
|
setState(RadioState.RX_SFD_SEARCH);
|
|
}
|
|
}
|
|
|
|
void writeTXFIFO(int data) {
|
|
if(txfifoFlush) {
|
|
txCursor = 0;
|
|
txfifoFlush = false;
|
|
}
|
|
if (DEBUG) log("Writing data: " + data + " to tx: " + txCursor);
|
|
|
|
if(txCursor == 0) {
|
|
if ((data & 0xff) > 127) {
|
|
logger.logw(this, WarningType.EXECUTION, "CC2520: Warning - packet size too large: " + (data & 0xff));
|
|
}
|
|
} else if (txCursor > 127) {
|
|
logger.logw(this, WarningType.EXECUTION, "CC2520: Warning - TX Cursor wrapped");
|
|
txCursor = 0;
|
|
}
|
|
memory[RAM_TXFIFO + txCursor] = data & 0xff;
|
|
txCursor++;
|
|
if (sendEvents) {
|
|
sendEvent("WRITE_TXFIFO", null);
|
|
}
|
|
}
|
|
|
|
// TODO: update any pins here?
|
|
void flushTX() {
|
|
txCursor = 0;
|
|
}
|
|
|
|
private void updateCCA() {
|
|
boolean oldCCA = currentCCA;
|
|
|
|
currentCCA = (status & STATUS_RSSI_VALID) > 0 && rssi < -95;
|
|
|
|
if (currentCCA != oldCCA) {
|
|
ccaGPIO.setActive(currentCCA);
|
|
if (currentCCA) {
|
|
memory[REG_FSMSTAT1] |= 1 << 4;
|
|
} else {
|
|
memory[REG_FSMSTAT1] &= ~(1 << 4);
|
|
}
|
|
if (DEBUG) log("Setting CCA to: " + currentCCA);
|
|
}
|
|
}
|
|
|
|
private void setSFD(boolean sfd) {
|
|
sfdGPIO.setActive(sfd);
|
|
if (sfd) {
|
|
memory[REG_FSMSTAT1] |= 1 << 5;
|
|
} else {
|
|
memory[REG_FSMSTAT1] &= ~(1 << 5);
|
|
}
|
|
if (DEBUG) log("SFD: " + sfd + " " + cpu.cycles);
|
|
}
|
|
|
|
private void setFIFOP(boolean fifop) {
|
|
currentFIFOP = fifop;
|
|
fifopGPIO.setActive(fifop);
|
|
if (fifop) {
|
|
memory[REG_FSMSTAT1] |= 1 << 6;
|
|
} else {
|
|
memory[REG_FSMSTAT1] &= ~(1 << 6);
|
|
}
|
|
if (DEBUG) log("Setting FIFOP to " + fifop);
|
|
}
|
|
|
|
private void setFIFO(boolean fifo) {
|
|
currentFIFO = fifo;
|
|
fifoGPIO.setActive(fifo);
|
|
if (fifo) {
|
|
memory[REG_FSMSTAT1] |= 1 << 7;
|
|
} else {
|
|
memory[REG_FSMSTAT1] &= ~(1 << 7);
|
|
}
|
|
if (DEBUG) log("Setting FIFO to " + fifo);
|
|
}
|
|
|
|
private void setRxOverflow() {
|
|
if (DEBUG) log("RXFIFO Overflow! Read Pos: " + rxFIFO.stateToString());
|
|
setFIFOP(true);
|
|
setFIFO(false);
|
|
setSFD(false);
|
|
overflow = true;
|
|
shouldAck = false;
|
|
setState(RadioState.RX_OVERFLOW);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* External APIs for simulators simulating Radio medium, etc.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
@Override
|
|
public boolean isReadyToReceive() {
|
|
return getState() == RadioState.RX_SFD_SEARCH;
|
|
}
|
|
|
|
private void updateActiveFrequency() {
|
|
/* INVERTED: f = 5 * (c - 11) + 357 + 0x4000 */
|
|
int freg = memory[REG_FREQCTRL] & 0x7f;
|
|
activeFrequency = freg + 2394;
|
|
activeChannel = 11 + (freg - 11) / 5;
|
|
}
|
|
|
|
@Override
|
|
public int getActiveFrequency() {
|
|
updateActiveFrequency();
|
|
return activeFrequency;
|
|
}
|
|
|
|
@Override
|
|
public int getActiveChannel() {
|
|
updateActiveFrequency();
|
|
return activeChannel;
|
|
}
|
|
|
|
/**
|
|
* This is actually the "CORR" value.
|
|
* @param lqi The Corr-val
|
|
* @sa CC2520 Datasheet
|
|
*/
|
|
@Override
|
|
public void setLQI(int lqi){
|
|
if(lqi < 0) {
|
|
lqi = 0;
|
|
} else if(lqi > 0x7f ) {
|
|
lqi = 0x7f;
|
|
}
|
|
corrval = lqi;
|
|
}
|
|
|
|
@Override
|
|
public int getLQI() {
|
|
return corrval;
|
|
}
|
|
|
|
@Override
|
|
public void setRSSI(int power) {
|
|
final int minp = -128 + RSSI_OFFSET;
|
|
final int maxp = 128 + RSSI_OFFSET;
|
|
if (power < minp) {
|
|
power = -minp;
|
|
}
|
|
if(power > maxp){
|
|
power = maxp;
|
|
}
|
|
|
|
if (DEBUG) log("external setRSSI to: " + power);
|
|
|
|
rssi = power;
|
|
memory[REG_RSSI] = power - RSSI_OFFSET;
|
|
updateCCA();
|
|
}
|
|
|
|
@Override
|
|
public int getRSSI() {
|
|
return rssi;
|
|
}
|
|
|
|
private boolean isDefinedTxPower(int txpower) {
|
|
switch (txpower) {
|
|
case 0xf7:
|
|
case 0xf2:
|
|
case 0xab:
|
|
case 0x88:
|
|
case 0x81:
|
|
case 0x32:
|
|
case 0x2c:
|
|
case 0x13:
|
|
case 0x03:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int getOutputPowerIndicator() {
|
|
// Higher TXPOWER value does not always mean higher transmission power.
|
|
// Instead of using the TXPOWER value, the output power is mapped into 9 classes.
|
|
int txpower = memory[REG_TXPOWER];
|
|
if (txpower >= 0xf7) {
|
|
return 9;
|
|
}
|
|
if (txpower >= 0xf2) {
|
|
return 8;
|
|
}
|
|
if (txpower >= 0xab) {
|
|
return 7;
|
|
}
|
|
if (txpower >= 0x88) {
|
|
return 3;
|
|
}
|
|
if (txpower >= 0x81) {
|
|
return 4;
|
|
}
|
|
if (txpower >= 0x32) {
|
|
return 5;
|
|
}
|
|
if (txpower >= 0x2c) {
|
|
return 2;
|
|
}
|
|
if (txpower >= 0x13) {
|
|
return 6;
|
|
}
|
|
if (txpower >= 0x03) {
|
|
return 1;
|
|
}
|
|
/* Unknown */
|
|
return 0;
|
|
// return memory[REG_TXPOWER];
|
|
}
|
|
|
|
@Override
|
|
public int getOutputPowerIndicatorMax() {
|
|
return 9;
|
|
// return 255;
|
|
}
|
|
|
|
@Override
|
|
public int getOutputPower() {
|
|
/* From CC2520 datasheet, table 17 */
|
|
int txpower = memory[REG_TXPOWER];
|
|
if (txpower >= 0xf7) {
|
|
return 5;
|
|
}
|
|
if (txpower >= 0xf2) {
|
|
return 3;
|
|
}
|
|
if (txpower >= 0xab) {
|
|
return 2;
|
|
}
|
|
if (txpower >= 0x88) {
|
|
return -4;
|
|
}
|
|
if (txpower >= 0x81) {
|
|
return -2;
|
|
}
|
|
if (txpower >= 0x32) {
|
|
return 0;
|
|
}
|
|
if (txpower >= 0x2c) {
|
|
return -7;
|
|
}
|
|
if (txpower >= 0x13) {
|
|
return 1;
|
|
}
|
|
if (txpower >= 0x03) {
|
|
return -18;
|
|
}
|
|
/* Unknown */
|
|
return -100;
|
|
}
|
|
|
|
@Override
|
|
public int getOutputPowerMax() {
|
|
return 5;
|
|
}
|
|
|
|
@Override
|
|
public void notifyReset() {
|
|
super.notifyReset();
|
|
setChipSelect(false);
|
|
status &= ~STATUS_TX_ACTIVE;
|
|
memory[REG_FSMSTAT1] &= ~(1 << 1);
|
|
setVRegOn(false);
|
|
reset();
|
|
}
|
|
|
|
public void setVRegOn(boolean newOn) {
|
|
if(isRadioOn == newOn) return;
|
|
|
|
if(newOn) {
|
|
// 0.6ms maximum vreg startup from datasheet pg 13
|
|
// but Z1 platform does not work with 0.1 so trying with lower...
|
|
cpu.scheduleTimeEventMillis(vregEvent, 0.05);
|
|
if (DEBUG) log("Scheduling vregEvent at: cyc = " + cpu.cycles +
|
|
" target: " + vregEvent.getTime() + " current: " + cpu.getTime());
|
|
} else {
|
|
isRadioOn = false;
|
|
setState(RadioState.VREG_OFF);
|
|
}
|
|
}
|
|
|
|
public void setChipSelect(boolean select) {
|
|
chipSelect = select;
|
|
if (!chipSelect) {
|
|
spiLen = 0;
|
|
if (command != null) {
|
|
command.executeSPICommand();
|
|
}
|
|
command = null;
|
|
}
|
|
|
|
if (DEBUG) {
|
|
log("ChipSelect: " + chipSelect);
|
|
}
|
|
}
|
|
|
|
public boolean getChipSelect() {
|
|
return chipSelect;
|
|
}
|
|
|
|
public void setGPIO(int index, IOPort port, int pin) {
|
|
gpio[index].setConfig(port, pin);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* Chip APIs
|
|
*****************************************************************************/
|
|
|
|
@Override
|
|
public int getModeMax() {
|
|
return MODE_MAX;
|
|
}
|
|
|
|
private String getLongAddress() {
|
|
StringBuilder sb = new StringBuilder();
|
|
for (int i = 0; i < 8; i++) {
|
|
if ((i % 2 == 0) && i > 0) {
|
|
sb.append(':');
|
|
}
|
|
sb.append(Utils.hex8(memory[RAM_IEEEADDR + 7 - i]));
|
|
}
|
|
return sb.toString();
|
|
}
|
|
|
|
@Override
|
|
public String info() {
|
|
updateActiveFrequency();
|
|
String commandStr = command == null ? "<waiting>" : command.name;
|
|
return " VREG_ON: " + isRadioOn + " Chip Select: " + chipSelect +
|
|
" OSC Stable: " + ((status & STATUS_XOSC16M_STABLE) > 0) +
|
|
" GPIO Polarity: 0x" + Utils.hex8(memory[REG_GPIOPOLARITY]) +
|
|
"\n RSSI Valid: " + ((status & STATUS_RSSI_VALID) > 0) + " CCA: " + currentCCA +
|
|
"\n FIFOP: " + currentFIFOP + " threshold: " + fifopThr + " FIFO: " + currentFIFO + " SFD: " + sfdGPIO.isActive() +
|
|
"\n " + rxFIFO.stateToString() + " expPacketLen: " + rxlen +
|
|
"\n Radio State: " + stateMachine + " SPI State: " + commandStr +
|
|
"\n AutoACK: " + autoAck + " AddrDecode: " + frameFilter + " AutoCRC: " + autoCRC +
|
|
"\n PanID: 0x" + Utils.hex8(memory[RAM_PANID + 1]) + Utils.hex8(memory[RAM_PANID]) +
|
|
" ShortAddr: 0x" + Utils.hex8(memory[RAM_SHORTADDR + 1]) + Utils.hex8(memory[RAM_SHORTADDR]) +
|
|
" LongAddr: 0x" + getLongAddress() +
|
|
"\n Channel: " + activeChannel +
|
|
" Output Power: " + getOutputPower() + "dB (" + getOutputPowerIndicator() + '/' + getOutputPowerIndicatorMax() +
|
|
") txpower: 0x" + Utils.hex8(memory[REG_TXPOWER]) +
|
|
"\n";
|
|
}
|
|
|
|
@Override
|
|
public void stateChanged(int state) {
|
|
}
|
|
|
|
/* return data in register at the correct position */
|
|
@Override
|
|
public int getConfiguration(int parameter) {
|
|
return memory[parameter];
|
|
}
|
|
|
|
|
|
/* For SPI Commands */
|
|
@Override
|
|
public int getSPIData(int offset) {
|
|
return spiData[offset];
|
|
}
|
|
|
|
@Override
|
|
public int getSPIDataLen() {
|
|
return spiLen;
|
|
}
|
|
|
|
@Override
|
|
public void outputSPI(int data) {
|
|
outputSPI = data;
|
|
}
|
|
|
|
/* reads one byte from RX fifo */
|
|
void readRXFifo() {
|
|
int fifoData = rxFIFO.read();
|
|
if (DEBUG) log("RXFIFO READ: " + rxFIFO.stateToString());
|
|
outputSPI = fifoData;
|
|
|
|
/* first check and clear FIFOP - since we now have read a byte! */
|
|
if (currentFIFOP && !overflow) {
|
|
/* FIFOP is lowered when rxFIFO is lower than or equal to fifopThr */
|
|
if(rxFIFO.length() <= fifopThr) {
|
|
if (DEBUG) log("*** FIFOP cleared at: " + rxFIFO.stateToString());
|
|
setFIFOP(false);
|
|
}
|
|
}
|
|
|
|
/* initiate read of another packet - update some variables to keep track of packet reading... */
|
|
if (rxfifoReadLeft == 0) {
|
|
rxfifoReadLeft = fifoData;
|
|
if (DEBUG) log("Init read of packet - len: " + rxfifoReadLeft +
|
|
" fifo: " + rxFIFO.stateToString());
|
|
} else if (--rxfifoReadLeft == 0) {
|
|
/* check if we have another packet in buffer */
|
|
if (rxFIFO.length() > 0) {
|
|
/* check if the packet is complete or longer than fifopThr */
|
|
if (rxFIFO.length() > rxFIFO.peek(0) ||
|
|
(rxFIFO.length() > fifopThr && !decodeAddress && !frameRejected)) {
|
|
if (DEBUG) log("More in FIFO - FIFOP = 1! plen: " + rxFIFO.stateToString());
|
|
if (!overflow) setFIFOP(true);
|
|
}
|
|
}
|
|
}
|
|
// Set the FIFO pin low if there are no more bytes available in the RXFIFO.
|
|
if (rxFIFO.length() == 0) {
|
|
if (DEBUG) log("Setting FIFO to low (buffer empty)");
|
|
setFIFO(false);
|
|
}
|
|
}
|
|
|
|
} // CC2520
|