//
// Copyright (c) 2010-2025 Antmicro
// Copyright (c) 2011-2015 Realtime Embedded
//
// This file is licensed under the MIT License.
// Full license text is available in 'licenses/MIT.txt'.
//
using System.Linq;
using Antmicro.Renode.Peripherals.Bus;
using Antmicro.Renode.Logging;
using System;
using System.Collections.Generic;
using Antmicro.Renode.Core;
using Antmicro.Renode.Utilities;
using Antmicro.Migrant;
using Antmicro.Renode.Peripherals.Wireless.IEEE802_15_4;
using Antmicro.Renode.Peripherals.Wireless.CC2538;
using Antmicro.Renode.Core.Structure.Registers;
namespace Antmicro.Renode.Peripherals.Wireless
{
public class CC2538RF : IDoubleWordPeripheral, IBytePeripheral, IKnownSize, IRadio
{
public CC2538RF()
{
rxLock = new object();
rxQueue = new Queue();
txQueue = new Queue();
shortAddress = new Address(AddressingMode.ShortAddress);
extendedAddress = new Address(AddressingMode.ExtendedAddress);
random = EmulationManager.Instance.CurrentEmulation.RandomGenerator;
IRQ = new GPIO();
srcShortEnabled = new bool[24];
srcExtendedEnabled = new bool[12];
matchedSourceAddresses = new bool[24];
srcShortPendEnabled = new bool[24];
srcExtendedPendEnabled = new bool[12];
ffsmMemory = new uint[96];
irqHandler = new InterruptHandler(IRQ);
irqHandler.RegisterInterrupt(InterruptRegister.IrqFlag0, InterruptSource.StartOfFrameDelimiter, 1);
irqHandler.RegisterInterrupt(InterruptRegister.IrqFlag0, InterruptSource.FifoP, 2);
irqHandler.RegisterInterrupt(InterruptRegister.IrqFlag0, InterruptSource.SrcMatchDone, 3);
irqHandler.RegisterInterrupt(InterruptRegister.IrqFlag0, InterruptSource.SrcMatchFound, 4);
irqHandler.RegisterInterrupt(InterruptRegister.IrqFlag0, InterruptSource.FrameAccepted, 5);
irqHandler.RegisterInterrupt(InterruptRegister.IrqFlag0, InterruptSource.RxPktDone, 6);
irqHandler.RegisterInterrupt(InterruptRegister.IrqFlag0, InterruptSource.RxMaskZero, 7);
irqHandler.RegisterInterrupt(InterruptRegister.IrqFlag1, InterruptSource.TxAckDone, 0);
irqHandler.RegisterInterrupt(InterruptRegister.IrqFlag1, InterruptSource.TxDone, 1);
irqHandler.RegisterInterrupt(InterruptRegister.IrqFlag1, InterruptSource.RfIdle, 2);
irqHandler.RegisterInterrupt(InterruptRegister.IrqFlag1, InterruptSource.CommandStrobeProcessorManualInterrupt, 3);
irqHandler.RegisterInterrupt(InterruptRegister.IrqFlag1, InterruptSource.CommandStrobeProcessorStop, 4);
irqHandler.RegisterInterrupt(InterruptRegister.IrqFlag1, InterruptSource.CommandStrobeProcessorWait, 5);
var matchedSourceIndex = new DoubleWordRegister(this);
matchedSourceIndexField = matchedSourceIndex.DefineValueField(0, 8, FieldMode.Read | FieldMode.Write);
var srcResMask = CreateRegistersGroup(3, this, 0, 8,
valueProviderCallback: ReadSrcResMaskRegister, writeCallback: WriteSrcResMaskRegister);
var srcExtendedAddressPendingEnabled = CreateRegistersGroup(3, this, 0, 8,
valueProviderCallback: ReadSrcExtendedAddressPendingEnabledRegister, writeCallback: WriteSrcExtendedAddressPendingEnabledRegister);
var srcShortAddressPendingEnabled = CreateRegistersGroup(3, this, 0, 8,
name: "SrcShortAddressPendingEnabled", valueProviderCallback: ReadSrcShortAddressPendingEnabledRegister, writeCallback: WriteSrcShortAddressPendingEnabledRegister);
var extAddress = CreateRegistersGroup(8, this, 0, 8,
valueProviderCallback: i => extendedAddress.Bytes[i], writeCallback: (i, @new) => extendedAddress.SetByte((byte)@new, i));
panId = CreateRegistersGroup(2, this, 0, 8);
var shortAddressRegister = CreateRegistersGroup(2, this, 0, 8,
valueProviderCallback: i => shortAddress.Bytes[i], writeCallback: (i, @new) => shortAddress.SetByte((byte)@new, i));
var sourceExtendedAddressEnable = CreateRegistersGroup(3, this, 0, 8,
valueProviderCallback: ReadSourceExtendedAddressEnableRegister, writeCallback: WriteSourceExtendedAddressEnableRegister);
var sourceShortAddressEnable = CreateRegistersGroup(3, this, 0, 8,
valueProviderCallback: ReadSourceShortAddressEnableRegister, writeCallback: WriteSourceShortAddressEnableRegister);
var frameHandling0 = new DoubleWordRegister(this, 0x40);
autoAck = frameHandling0.DefineFlagField(5);
autoCrc = frameHandling0.DefineFlagField(6);
appendDataMode = frameHandling0.DefineFlagField(7);
var frameHandling1 = new DoubleWordRegister(this, 0x1);
pendingOr = frameHandling1.DefineFlagField(2);
var sourceAddressMatching = new DoubleWordRegister(this, 0x7);
sourceAddressMatchingEnabled = sourceAddressMatching.DefineFlagField(0);
autoPendEnabled = sourceAddressMatching.DefineFlagField(1);
pendDataRequestOnly = sourceAddressMatching.DefineFlagField(2);
var radioStatus0 = new DoubleWordRegister(this, 0).WithValueField(0, 6, FieldMode.Read, valueProviderCallback: _ => (uint)fsmState)
.WithFlag(7, FieldMode.Read, valueProviderCallback: _ => true);
var radioStatus1 = new DoubleWordRegister(this, 0).WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => ReadRadioStatus1Register());
var rssiValidStatus = new DoubleWordRegister(this, 0x1).WithFlag(0, FieldMode.Read);
var interruptMask = CreateRegistersGroup(2, this, 0, 8,
valueProviderCallback: i => irqHandler.GetRegisterMask(InterruptRegisterHelper.GetMaskRegister(i)),
writeCallback: (i, @new) => { irqHandler.SetRegisterMask(InterruptRegisterHelper.GetMaskRegister(i), (uint)@new); });
var randomData = new DoubleWordRegister(this, 0).WithValueField(0, 2, FieldMode.Read, valueProviderCallback: _ => (uint)(random.Next() & 3));
var frameFiltering0 = new DoubleWordRegister(this, 0xD);
frameFilterEnabled = frameFiltering0.DefineFlagField(0);
isPanCoordinator = frameFiltering0.DefineFlagField(1);
maxFrameVersion = frameFiltering0.DefineValueField(2, 2);
var frameFiltering1 = new DoubleWordRegister(this, 0x78);
acceptBeaconFrames = frameFiltering1.DefineFlagField(3);
acceptDataFrames = frameFiltering1.DefineFlagField(4);
acceptAckFrames = frameFiltering1.DefineFlagField(5);
acceptMacCmdFrames = frameFiltering1.DefineFlagField(6);
//Reset value set according to the documentation, but register value is caluculated from Channel value.
var frequencyControl = new DoubleWordRegister(this, 0xB).WithValueField(0, 7,
changeCallback: (_, value) => Channel = (int)(((value > 113 ? 113 : value) - 11) / 5 + 11), //FREQ = 11 + 5(channel - 11), maximum value is 113.
valueProviderCallback: (value) => 11 + 5 * ((uint)Channel - 11));
var rfData = new DoubleWordRegister(this, 0).WithValueField(0, 8,
valueProviderCallback: _ => DequeueData(), writeCallback: (_, @new) => { EnqueueData((byte)@new); });
var interruptFlag = CreateRegistersGroup(2, this, 0, 8,
valueProviderCallback: i => irqHandler.GetRegisterValue(InterruptRegisterHelper.GetValueRegister(i)),
writeCallback: (i, @new) => { irqHandler.SetRegisterValue(InterruptRegisterHelper.GetValueRegister(i), (uint)@new); });
var commandStrobeProcessor = new DoubleWordRegister(this, 0).WithValueField(0, 8, FieldMode.Write, writeCallback: (_, @new) => { HandleSFRInstruction((uint)@new); });
var rxFifoBytesCount = new DoubleWordRegister(this).WithValueField(0, 8, FieldMode.Read, valueProviderCallback: (_) => GetRxFifoBytesCount());
var addresses = new Dictionary
{
{ (uint)Register.RfData, rfData },
{ (uint)Register.CommandStrobeProcessor, commandStrobeProcessor },
{ (uint)Register.FrameFiltering0, frameFiltering0 },
{ (uint)Register.FrameFiltering1, frameFiltering1 },
{ (uint)Register.SourceAddressMatching, sourceAddressMatching },
{ (uint)Register.FrameHandling0, frameHandling0 },
{ (uint)Register.FrameHandling1, frameHandling1 },
{ (uint)Register.RadioStatus0, radioStatus0 },
{ (uint)Register.RadioStatus1, radioStatus1 },
{ (uint)Register.RssiValidStatus, rssiValidStatus },
{ (uint)Register.RandomData, randomData },
{ (uint)Register.SourceAddressMatchingResult, matchedSourceIndex },
{ (uint)Register.FrequencyControl, frequencyControl },
{ (uint)Register.RxFifoBytesCount, rxFifoBytesCount }
};
RegisterGroup(addresses, (uint)Register.InterruptFlag, interruptFlag);
RegisterGroup(addresses, (uint)Register.SourceExtendedAdressEnable, sourceExtendedAddressEnable);
RegisterGroup(addresses, (uint)Register.SourceShortAddressEnable, sourceShortAddressEnable);
RegisterGroup(addresses, (uint)Register.InterruptMask, interruptMask);
RegisterGroup(addresses, (uint)Register.SourceAddressMatchingResultMask, srcResMask);
RegisterGroup(addresses, (uint)Register.SourceExtendedAddressPendingEnabled, srcExtendedAddressPendingEnabled);
RegisterGroup(addresses, (uint)Register.SourceShortAddressPendingEnabled, srcShortAddressPendingEnabled);
RegisterGroup(addresses, (uint)Register.ExtendedAddress, extAddress);
RegisterGroup(addresses, (uint)Register.PanId, panId);
RegisterGroup(addresses, (uint)Register.ShortAddressRegister, shortAddressRegister);
registers = new DoubleWordRegisterCollection(this, addresses);
Reset();
}
public uint ReadDoubleWord(long offset)
{
uint result = 0u;
if(offset >= FfsmMemoryStart && offset <= FfsmMemoryEnd)
{
result = ffsmMemory[offset - FfsmMemoryStart];
}
else if(offset < RxFifoMemorySize)
{
lock(rxLock)
{
// dividedOffset represents the position in the packet queue, assuming that the "Length" byte is also stored there.
// Since we do not store the lenght along with the packet, we handle it with increasedFrameIndex variable.
// The offset is shifted, as it seems that the driver accesses each byte as it was a double word value.
var dividedOffset = offset >> 2;
var increasedFrameIndex = 1;
do
{
if(rxQueue.Count < increasedFrameIndex)
{
break;
}
if(dividedOffset == increasedFrameIndex - 1)
{
//return size of the current frame
result = (uint)rxQueue.ElementAt(increasedFrameIndex - 1).Bytes.Count();
break;
}
if(increasedFrameIndex + rxQueue.ElementAt(increasedFrameIndex - 1).Bytes.Count() > dividedOffset)
{
//return specific byte from the current frame
result = rxQueue.ElementAt(increasedFrameIndex - 1).Bytes[dividedOffset - increasedFrameIndex];
break;
}
dividedOffset -= rxQueue.ElementAt(increasedFrameIndex - 1).Bytes.Count();
increasedFrameIndex++;
} while(dividedOffset > 0);
}
}
else
{
result = registers.Read(offset);
}
return result;
}
public void WriteDoubleWord(long offset, uint value)
{
if(offset >= 0x400 && offset <= 0x57C)
{
ffsmMemory[offset - 0x400] = value;
return;
}
registers.Write(offset, value);
return;
}
//used by uDMA
public byte ReadByte(long offset)
{
if(offset == (long)Register.RfData)
{
return DequeueData();
}
this.Log(LogLevel.Warning, "{0} does not implement byte reads apart from {1} (0x{2:X}).", GetType().Name, "RFData", (long)Register.RfData);
this.LogUnhandledRead(offset);
return 0;
}
//used by uDMA
public void WriteByte(long offset, byte value)
{
if(offset == (long)Register.RfData) // RF data
{
EnqueueData(value);
return;
}
this.Log(LogLevel.Warning, "{0} does not implement byte writes apart from {1} (0x{2:X}).", GetType().Name, "RFData", (long)Register.RfData);
this.LogUnhandledWrite(offset, value);
}
public void Reset()
{
lock(rxLock)
{
registers.Reset();
currentFrameOffset = -1;
txPendingCounter = 0;
fsmState = FSMStates.Idle;
rxQueue.Clear();
Array.Clear(srcShortEnabled, 0, srcShortEnabled.Length);
Array.Clear(srcShortPendEnabled, 0, srcShortPendEnabled.Length);
Array.Clear(matchedSourceAddresses, 0, matchedSourceAddresses.Length);
Array.Clear(srcExtendedEnabled, 0, srcExtendedEnabled.Length);
Array.Clear(srcExtendedPendEnabled, 0, srcExtendedPendEnabled.Length);
Array.Clear(ffsmMemory, 0, ffsmMemory.Length);
irqHandler.Reset();
txQueue.Clear();
Channel = ChannelResetValue;
}
}
public void ReceiveFrame(byte[] bytes, IRadio sender)
{
irqHandler.RequestInterrupt(InterruptSource.StartOfFrameDelimiter);
Frame ackFrame = null;
var frame = new Frame(bytes);
var crcOK = frame.CheckCRC();
if(autoCrc.Value && !crcOK)
{
this.Log(LogLevel.Warning, "Received frame with wrong CRC");
}
if(frameFilterEnabled.Value)
{
if(!ShouldWeAcceptThisFrame(frame))
{
this.DebugLog("Not accepting a frame");
return;
}
irqHandler.RequestInterrupt(InterruptSource.FrameAccepted);
}
lock(rxLock)
{
var autoPendingBit = false;
var index = NoSourceIndex;
if(sourceAddressMatchingEnabled.Value && crcOK) //todo: verify if this should not only run when frameFilterEnabled, as in CC2520
{
switch(frame.SourceAddressingMode)
{
case AddressingMode.ShortAddress:
for(var i = 0u; i < srcShortEnabled.Length; i++)
{
if(!srcShortEnabled[i])
{
continue;
}
if(frame.AddressInformation.SourcePan == GetShortPanIdFromRamTable(i)
&& frame.AddressInformation.SourceAddress.GetValue() == GetShortSourceAddressFromRamTable(i))
{
matchedSourceAddresses[i] = true;
autoPendingBit |= srcShortPendEnabled[i];
if(index == NoSourceIndex)
{
index = i;
}
}
}
break;
case AddressingMode.ExtendedAddress:
for(var i = 0u; i < srcExtendedEnabled.Length; i++)
{
if(!srcExtendedEnabled[i])
{
continue;
}
if(frame.AddressInformation.SourceAddress.GetValue() == GetExtendedSourceAddressFromRamTable(i))
{
matchedSourceAddresses[2 * i] = true;
matchedSourceAddresses[2 * i + 1] = true;
autoPendingBit |= srcExtendedPendEnabled[i];
if(index == NoSourceIndex)
{
index = i | 0x20;
}
}
}
break;
}
matchedSourceIndexField.Value = index;
autoPendingBit &= autoPendEnabled.Value
&& frameFilterEnabled.Value
&& (!pendDataRequestOnly.Value
|| (frame.Type == FrameType.MACControl
&& frame.Payload.Count > 0
&& frame.Payload[0] == 0x4));
BitHelper.SetBit(ref index, 6, autoPendingBit);
if(index != NoSourceIndex)
{
irqHandler.RequestInterrupt(InterruptSource.SrcMatchFound);
}
irqHandler.RequestInterrupt(InterruptSource.SrcMatchDone);
}
if(autoCrc.Value)
{
var rssi = 70; // why 70?
var secondByte = crcOK ? (1u << 7) : 0;
if(appendDataMode.Value)
{
secondByte |= index & 0x7F;
}
else
{
secondByte |= 100; // correlation value 100 means near maximum quality
}
frame.Bytes[frame.Bytes.Length - 2] = (byte)rssi;
frame.Bytes[frame.Bytes.Length - 1] = (byte)secondByte;
}
rxQueue.Enqueue(frame);
irqHandler.RequestInterrupt(InterruptSource.FifoP);
if(crcOK && autoAck.Value
&& frame.AcknowledgeRequest
&& frame.Type != FrameType.Beacon
&& frame.Type != FrameType.ACK)
{
ackFrame = Frame.CreateACK(frame.DataSequenceNumber, pendingOr.Value | autoPendingBit);
}
}
var frameSent = FrameSent;
if(frameSent != null && ackFrame != null)
{
frameSent(this, ackFrame.Bytes);
irqHandler.RequestInterrupt(InterruptSource.TxAckDone);
}
irqHandler.RequestInterrupt(InterruptSource.RxPktDone);
}
public int Channel { get; set; }
public event Action FrameSent;
public GPIO IRQ { get; private set; }
public long Size { get { return 0x1000; } }
private uint GetRxFifoBytesCount()
{
lock(rxLock)
{
//takes only first packet into account plus 1 byte that indicates its size
return rxQueue.Count > 0 ? (uint)rxQueue.Peek().Bytes.Length + 1 : 0u;
}
}
private static DoubleWordRegister[] CreateRegistersGroup(int size, IPeripheral parent, int position, int width,
FieldMode mode = FieldMode.Read | FieldMode.Write, Action writeCallback = null, Func valueProviderCallback = null, string name = null)
{
var result = new DoubleWordRegister[size];
for(var i = 0; i < size; i++)
{
var j = i;
result[i] = new DoubleWordRegister(parent)
.WithValueField(position, width, mode, name: name + j,
valueProviderCallback: _ =>
{
if(valueProviderCallback != null)
{
return valueProviderCallback(j);
}
return 0;
},
writeCallback: (_, @new) =>
{
if(writeCallback != null)
{
writeCallback(j, (uint)@new);
}
});
}
return result;
}
private static void RegisterGroup(Dictionary collection, long initialAddress, DoubleWordRegister[] group)
{
for(var i = 0; i < group.Length; i++)
{
collection.Add(initialAddress + 0x4 * i, group[i]);
}
}
private uint ReadRadioStatus1Register()
{
if(txPendingCounter > 0)
{
txPendingCounter--;
}
lock(rxLock)
{
return (rxQueue.Count == 0 ? 0u : 3 << 6) // FIFO, FIFOP
| 1u << 4 // clear channel assessment
| (txPendingCounter > 0 ? 2u : 0);
}
}
private void WriteSourceShortAddressEnableRegister(int index, uint value)
{
for(byte i = 0; i < 8; i++)
{
srcShortEnabled[i + 8 * index] = BitHelper.IsBitSet(value, i);
}
}
private uint ReadSourceShortAddressEnableRegister(int index)
{
var result = 0u;
for(byte i = 0; i < 8; i++)
{
if(srcShortEnabled[i + 8 * index])
{
BitHelper.SetBit(ref result, i, true);
}
}
return result;
}
private void WriteSourceExtendedAddressEnableRegister(int index, uint value)
{
for(var i = 0; i < 4; i++)
{
srcExtendedEnabled[i + 4 * index] = BitHelper.IsBitSet(value, (byte)(2 * i));
}
}
private uint ReadSourceExtendedAddressEnableRegister(int index)
{
var result = 0u;
for(var i = 0; i < 4; i++)
{
if(srcExtendedEnabled[i + 4 * index])
{
BitHelper.SetBit(ref result, (byte)(2 * i), true);
}
}
return result;
}
private uint ReadSrcResMaskRegister(int id)
{
var result = 0u;
for(byte i = 0; i < 8; i++)
{
BitHelper.SetBit(ref result, i, matchedSourceAddresses[i + 8 * id]);
}
return result;
}
private void WriteSrcResMaskRegister(int index, uint value)
{
for(byte i = 0; i < 8; i++)
{
matchedSourceAddresses[i + 8 * index] = BitHelper.IsBitSet(value, i);
}
}
private uint ReadSrcExtendedAddressPendingEnabledRegister(int index)
{
var result = 0u;
for(var i = 0; i < 4; i++)
{
if(srcExtendedPendEnabled[i + 4 * index])
{
BitHelper.SetBit(ref result, (byte)(2 * i), true);
}
}
return result;
}
private void WriteSrcExtendedAddressPendingEnabledRegister(int index, uint value)
{
for(var i = 0; i < 4; i++)
{
srcExtendedPendEnabled[i + 4 * index] = BitHelper.IsBitSet(value, (byte)(2 * i));
}
}
private uint ReadSrcShortAddressPendingEnabledRegister(int index)
{
var result = 0u;
for(byte i = 0; i < 8; i++)
{
BitHelper.SetBit(ref result, i, srcShortPendEnabled[i + 8 * index]);
}
return result;
}
private void WriteSrcShortAddressPendingEnabledRegister(int index, uint value)
{
for(byte i = 0; i < 8; i++)
{
srcShortPendEnabled[i + 8 * index] = BitHelper.IsBitSet(value, i);
}
}
private void HandleSFRInstruction(uint value)
{
switch((CSPInstructions)value)
{
case CSPInstructions.TxOn:
fsmState = FSMStates.Tx;
txPendingCounter = TxPendingCounterInitialValue;
SendData();
break;
case CSPInstructions.RxOn:
fsmState = FSMStates.Rx;
break;
case CSPInstructions.RfOff:
fsmState = FSMStates.Idle;
break;
case CSPInstructions.RxFifoFlush:
lock(rxLock)
{
if(rxQueue.Count != 0)
{
this.Log(LogLevel.Warning, "Dropping unreceived frame.");
currentFrameOffset = -1;
rxQueue.Clear();
}
}
break;
case CSPInstructions.TxFifoFlush:
txQueue.Clear();
break;
default:
this.Log(LogLevel.Warning, "Unsupported CSP instruction {0}.", value);
break;
}
}
private byte DequeueData()
{
lock(rxLock)
{
if(rxQueue.Count == 0)
{
this.Log(LogLevel.Warning, "Trying to dequeue data from empty RX FIFO.");
return 0x0;
}
var currentFrame = rxQueue.Peek();
if(currentFrameOffset == -1)
{
// we need to send packet length first
currentFrameOffset++;
return currentFrame.Length;
}
var result = currentFrame.Bytes[currentFrameOffset++];
if(currentFrameOffset == currentFrame.Bytes.Length)
{
rxQueue.Dequeue();
currentFrameOffset = -1;
if(rxQueue.Count > 0)
{
irqHandler.RequestInterrupt(InterruptSource.FifoP);
}
}
return result;
}
}
private void EnqueueData(byte value)
{
this.NoisyLog("Enqueuing data: 0x{0:X}.", value);
txQueue.Enqueue((byte)(value & 0xFF));
}
private void SendData()
{
if(txQueue.Count == 0)
{
this.Log(LogLevel.Warning, "Attempted to transmit an empty frame.");
return;
}
irqHandler.RequestInterrupt(InterruptSource.StartOfFrameDelimiter);
//ignore the first byte, it's length. Don't drop it though, as the same packet might get resent.
var crc = Frame.CalculateCRC(txQueue.Skip(1));
var frame = new Frame(txQueue.Skip(1).Concat(crc).ToArray());
this.DebugLog("Sending frame {0}.", frame.Bytes.Select(x => "0x{0:X}".FormatWith(x)).Stringify());
var fs = FrameSent;
if(fs != null)
{
fs.Invoke(this, frame.Bytes);
}
else
{
this.Log(LogLevel.Warning, "FrameSent is not initialized. Am I connected to medium?");
}
irqHandler.RequestInterrupt(InterruptSource.TxDone);
}
private bool ShouldWeAcceptThisFrame(Frame frame)
{
// (1) check if length is ok
// (2) check reserved FCF bits
// for now we assume it is fine - let's be optimistic. Note - it is implemented in CC2520.
// (3) check FCF version
if(frame.FrameVersion > maxFrameVersion.Value)
{
this.Log(LogLevel.Noisy, "Wrong frame version.");
return false;
}
// (4) check source/destination addressing mode
if(frame.SourceAddressingMode == AddressingMode.Reserved || frame.DestinationAddressingMode == AddressingMode.Reserved)
{
this.Log(LogLevel.Noisy, "Wrong addressing mode.");
return false;
}
// (5) check destination address
if(frame.DestinationAddressingMode != AddressingMode.None)
{
// (5.1) check destination PAN
if(frame.AddressInformation.DestinationPan != BroadcastPanIdentifier && frame.AddressInformation.DestinationPan != GetPanId())
{
this.Log(LogLevel.Noisy, "Wrong destination PAN.");
return false;
}
// (5.2) check destination short address
if(frame.DestinationAddressingMode == AddressingMode.ShortAddress)
{
if(!frame.AddressInformation.DestinationAddress.IsShortBroadcast && !frame.AddressInformation.DestinationAddress.Equals(shortAddress))
{
this.Log(LogLevel.Noisy, "Wrong destination short address.");
return false;
}
}
// (5.3) check destination extended address
else if(frame.DestinationAddressingMode == AddressingMode.ExtendedAddress)
{
if(!frame.AddressInformation.DestinationAddress.Equals(extendedAddress))
{
this.Log(LogLevel.Noisy, "Wrong destination extended address (i'm {0}, but the message is directed to {1}.", extendedAddress.GetValue(), frame.AddressInformation.DestinationAddress.GetValue());
return false;
}
}
}
// (6) check frame type
//todo: not implemented reserved types (implemented in cc2520)
switch(frame.Type)
{
case FrameType.Beacon:
if(!acceptBeaconFrames.Value
|| frame.Length < 9
|| frame.DestinationAddressingMode != AddressingMode.None
|| (frame.SourceAddressingMode != AddressingMode.ShortAddress && frame.SourceAddressingMode != AddressingMode.ExtendedAddress)
|| (frame.AddressInformation.SourcePan != BroadcastPanIdentifier && frame.AddressInformation.SourcePan != GetPanId()))
{
this.Log(LogLevel.Noisy, "Wrong beacon frame.");
return false;
}
break;
case FrameType.Data:
if(!acceptDataFrames.Value
|| frame.Length < 9
|| (frame.DestinationAddressingMode == AddressingMode.None
&& (!isPanCoordinator.Value || frame.AddressInformation.SourcePan != GetPanId())))
{
this.Log(LogLevel.Noisy, "Wrong data frame.");
return false;
}
break;
case FrameType.ACK:
if(!acceptAckFrames.Value || frame.Length != 5)
{
this.Log(LogLevel.Noisy, "Wrong ACK frame.");
return false;
}
break;
case FrameType.MACControl:
if(!acceptMacCmdFrames.Value
|| frame.Length < 9
|| (frame.DestinationAddressingMode == AddressingMode.None
&& (!isPanCoordinator.Value || frame.AddressInformation.SourcePan != GetPanId())))
{
this.Log(LogLevel.Noisy, "Wrong MAC control frame.");
return false;
}
break;
default:
return false;
}
return true;
}
private uint GetPanId()
{
return (panId[1].Value << 8) | panId[0].Value;
}
private ushort GetShortPanIdFromRamTable(uint id)
{
return (ushort)((ffsmMemory[16 * id] << 8) | (ffsmMemory[16 * id + 1]));
}
private ushort GetShortSourceAddressFromRamTable(uint id)
{
return (ushort)((ffsmMemory[16 * id + 2] << 8) | (ffsmMemory[16 * id + 3]));
}
private ulong GetExtendedSourceAddressFromRamTable(uint id)
{
return ((ffsmMemory[32 * id] << 7 * 8) | (ffsmMemory[32 * id + 1] << 6 * 8)
| (ffsmMemory[32 * id + 2] << 5 * 8) | (ffsmMemory[32 * id + 3] << 4 * 8)
| (ffsmMemory[32 * id + 4] << 3 * 8) | (ffsmMemory[32 * id + 5] << 2 * 8)
| (ffsmMemory[32 * id + 6] << 8) | ffsmMemory[32 * id + 7]);
}
private int txPendingCounter;
private int currentFrameOffset;
private FSMStates fsmState;
private readonly DoubleWordRegisterCollection registers;
private readonly IFlagRegisterField autoAck;
private readonly IFlagRegisterField autoCrc;
private readonly IFlagRegisterField frameFilterEnabled;
private readonly IFlagRegisterField sourceAddressMatchingEnabled;
private readonly IFlagRegisterField autoPendEnabled;
private readonly IFlagRegisterField pendDataRequestOnly;
private readonly IFlagRegisterField appendDataMode;
private readonly IFlagRegisterField pendingOr;
private readonly IValueRegisterField matchedSourceIndexField;
private readonly IFlagRegisterField acceptBeaconFrames;
private readonly IFlagRegisterField acceptAckFrames;
private readonly IFlagRegisterField acceptDataFrames;
private readonly IFlagRegisterField acceptMacCmdFrames;
private readonly IFlagRegisterField isPanCoordinator;
private readonly IValueRegisterField maxFrameVersion;
private readonly DoubleWordRegister[] panId;
private readonly bool[] srcShortEnabled;
private readonly bool[] srcShortPendEnabled;
private readonly bool[] srcExtendedEnabled;
private readonly bool[] srcExtendedPendEnabled;
private readonly bool[] matchedSourceAddresses;
private readonly uint[] ffsmMemory;
private readonly object rxLock;
private readonly InterruptHandler irqHandler;
private readonly Address shortAddress;
private readonly Address extendedAddress;
private readonly Queue rxQueue;
private readonly Queue txQueue;
[Constructor]
private readonly PseudorandomNumberGenerator random;
private const uint NoSourceIndex = 0x3F;
private const int BroadcastPanIdentifier = 0xFFFF;
private const int RamTableBaseAddress = 0x40088400;
//HACK! TX_ACTIVE is required to be set as 1 few times in a row for contiki
private const int TxPendingCounterInitialValue = 4;
private const int ChannelResetValue = 11;
private const uint FfsmMemoryStart = 0x400;
private const uint FfsmMemoryEnd = 0x57C;
private const uint RxFifoMemorySize = 0x200;
private enum CSPInstructions
{
RfOff = 0xDF,
RxOn = 0xE3,
TxOn = 0xE9,
RxFifoFlush = 0xED,
TxFifoFlush = 0xEE
}
private enum FSMStates
{
Idle = 0x00,
Rx = 0x07,
Tx = 0x22
}
private enum Register
{
//not groupped
SourceAddressMatchingResult = 0x58C,
FrameFiltering0 = 0x600,
FrameFiltering1 = 0x604,
SourceAddressMatching = 0x608,
FrameHandling0 = 0x624,
FrameHandling1 = 0x628,
FrequencyControl = 0x63C,
RadioStatus0 = 0x648,
RadioStatus1 = 0x64C,
RssiValidStatus = 0x664,
RandomData = 0x69C,
RfData = 0x828,
CommandStrobeProcessor = 0x838,
RxFifoBytesCount = 0x66C,
//register groups
SourceAddressMatchingResultMask = 0x580,
SourceExtendedAddressPendingEnabled = 0x590,
SourceShortAddressPendingEnabled = 0x59C,
ExtendedAddress = 0x5A8,
PanId = 0x5C8,
ShortAddressRegister = 0x5D0,
SourceShortAddressEnable = 0x60C,
SourceExtendedAdressEnable = 0x618,
InterruptMask = 0x68C,
InterruptFlag = 0x830,
}
}
}