1 //
2 // Copyright (c) 2010-2025 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 using System;
8 using System.Collections.Concurrent;
9 using System.Collections.Generic;
10 using Antmicro.Renode.Core;
11 using Antmicro.Renode.Core.Structure.Registers;
12 using Antmicro.Renode.Exceptions;
13 using Antmicro.Renode.Logging;
14 using Antmicro.Renode.Peripherals.Bus;
15 using Antmicro.Renode.Peripherals.Miscellaneous;
16 using Antmicro.Renode.Time;
17 using Antmicro.Renode.Utilities;
18 
19 namespace Antmicro.Renode.Peripherals.Wireless
20 {
21     public class NRF52840_Radio : BasicDoubleWordPeripheral, IRadio, IKnownSize, INRFEventProvider
22     {
NRF52840_Radio(IMachine machine)23         public NRF52840_Radio(IMachine machine) : base(machine)
24         {
25             IRQ = new GPIO();
26             interruptManager = new InterruptManager<Events>(this, IRQ, "RadioIrq");
27             shorts = new Shorts();
28             events = new IFlagRegisterField[(int)Events.PHYEnd + 1];
29             rxBuffer = new ConcurrentQueue<KeyValuePair<byte[], IRadio>>();
30             DefineRegisters();
31             Reset();
32         }
33 
Reset()34         public override void Reset()
35         {
36             radioState = State.Disabled;
37             interruptManager.Reset();
38             base.Reset();
39             addressPrefixes = new byte[8];
40             while(rxBuffer.TryDequeue(out var _)) { }
41         }
42 
FakePacket()43         public void FakePacket()
44         {
45             ReceiveFrame(new byte[]{0xD6, 0xBE, 0x89, 0x8E, 0x60, 0x11, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0xC0, 0x2, 0x1, 0x6, 0x7, 0x3, 0xD, 0x18, 0xF, 0x18, 0xA, 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, null);
46         }
47 
ReceiveFrame(byte[] frame, IRadio sender)48         public void ReceiveFrame(byte[] frame, IRadio sender)
49         {
50             if(radioState != State.RxIdle)
51             {
52                 rxBuffer.Enqueue(new KeyValuePair<byte[], IRadio>(frame, sender));
53                 return;
54             }
55 
56             var addressLength = (int)baseAddressLength.Value + 1;
57             var headerLengthInAir = HeaderLengthInAir();
58             var headerLengthInRAM = HeaderLengthInRAM();
59 
60             var dataAddress = (uint)packetPointer.Value;
61             sysbus.WriteBytes(frame, address: dataAddress, startingIndex: addressLength, count: headerLengthInRAM);
62             var payloadLength = Math.Min(frame[addressLength + (int)s0Length.Value], (byte)maxPacketLength.Value);
63             sysbus.WriteBytes(frame, address: (ulong)(dataAddress + headerLengthInRAM), startingIndex: addressLength + headerLengthInAir, count: payloadLength);
64 
65             var crcLen = 4;
66             ScheduleRadioEvents((uint)(headerLengthInAir + payloadLength + crcLen));
67         }
68 
69         public event Action<IRadio, byte[]> FrameSent;
70 
71         public event Action<uint> EventTriggered;
72 
73         public int Channel
74         {
75             get
76             {
77                 return bluetoothLEChannelMap.TryGetValue(Frequency, out var result)
78                     ? result
79                     : -1;
80             }
81 
82             set
83             {
84                 throw new RecoverableException("Setting channel manually is not supported");
85             }
86         }
87 
88         public long Size => 0x1000;
89 
90         public uint Frequency => (frequencyMap.Value ? 2360 : 2400U) + (uint)frequency.Value;
91 
92         public GPIO IRQ { get; }
93 
DefineTask(Registers register, Action callback, string name)94         private void DefineTask(Registers register, Action callback, string name)
95         {
96             register.Define(this, name: name)
97                 .WithFlag(0, FieldMode.Write, writeCallback: (_, value) => { if(value) callback(); })
98                 .WithReservedBits(1, 31)
99             ;
100         }
101 
DefineEvent(Registers register, Action callbackOnSet, Events @event, string name)102         private void DefineEvent(Registers register, Action callbackOnSet, Events @event, string name)
103         {
104             register.Define(this, name: name)
105                 .WithFlag(0, out events[(int)@event], writeCallback: (_, value) =>
106                 {
107                     if(value)
108                     {
109                         callbackOnSet();
110                     }
111                     else
112                     {
113                         interruptManager.SetInterrupt(@event, false);
114                     }
115                 })
116                 .WithReservedBits(1, 31)
117             ;
118         }
119 
DefineRegisters()120         private void DefineRegisters()
121         {
122             DefineTask(Registers.TxEnable, TxEnable, "TASKS_TXEN");
123 
124             DefineTask(Registers.RxEnable, RxEnable, "TASKS_RXEN");
125 
126             DefineTask(Registers.Start, Start, "TASKS_START");
127 
128             DefineTask(Registers.Disable, Disable, "TASKS_DISABLE");
129 
130             DefineEvent(Registers.Ready, () => this.Log(LogLevel.Error, "Trying to trigger READY event, not supported"), Events.Ready, "EVENTS_READY");
131             DefineEvent(Registers.AddressSentOrReceived, () => this.Log(LogLevel.Error, "Trying to trigger ADDRESS event, not supported"), Events.Address, "EVENTS_ADDRESS");
132             DefineEvent(Registers.PayloadSentOrReceived, () => this.Log(LogLevel.Error, "Trying to trigger PAYLOAD event, not supported"), Events.Payload, "EVENTS_PAYLOAD");
133             DefineEvent(Registers.PacketSentOrReceived, () => this.Log(LogLevel.Error, "Trying to trigger END event, not supported"), Events.End, "EVENTS_END");
134             DefineEvent(Registers.BitCounterMatch, () => this.Log(LogLevel.Error, "Trying to trigger BCMATCH event, not supported"), Events.BitCountMatch, "EVENTS_BCMATCH");
135             DefineEvent(Registers.RSSIEnd, () => this.Log(LogLevel.Error, "Trying to trigger RSSIEnd event, not supported"), Events.RSSIEnd, "EVENTS_RSSIEND");
136             DefineEvent(Registers.CRCOk, () => this.Log(LogLevel.Error, "Trying to trigger CRCOk event, not supported"), Events.CRCOk, "EVENTS_CRCOK");
137 
138             DefineEvent(Registers.RadioDisabled, Disable, Events.Disabled, "EVENTS_DISABLED");
139 
140             DefineEvent(Registers.TxReady, TxEnable, Events.TxReady, "EVENTS_TXREADY");
141 
142             DefineEvent(Registers.RxReady, RxEnable, Events.RxReady, "EVENTS_RXREADY");
143 
144             // Notice: while the whole Shorts register is implemented, we don't necessarily
145             // support all the mentioned events and tasks
146             Registers.Shorts.Define(this)
147                 .WithFlag(0, out shorts.ReadyStart, name: "READY_START")
148                 .WithFlag(1, out shorts.EndDisable, name: "END_DISABLE")
149                 .WithFlag(2, out shorts.DisabledTxEnable, name: "DISABLED_TXEN")
150                 .WithFlag(3, out shorts.DisabledRxEnable, name: "DISABLED_RXEN")
151                 .WithFlag(4, out shorts.AddressRSSIStart, name: "ADDRESS_RSSISTART")
152                 .WithFlag(5, out shorts.EndStart, name: "END_START")
153                 .WithFlag(6, out shorts.AddressBitCountStart, name: "ADDRESS_BCSTART")
154                 .WithFlag(8, out shorts.DisabledRSSIStop, name: "DISABLED_RSSISTOP")
155                 .WithFlag(11, out shorts.RxReadyCCAStart, name: "RXREADY_CCASTART")
156                 .WithFlag(12, out shorts.CCAIdleTxEnable, name: "CCAIDLE_TXEN")
157                 .WithFlag(13, out shorts.CCABusyDisable, name: "CCABUSY_DISABLE")
158                 .WithFlag(14, out shorts.FrameStartBitCountStart, name: "FRAMESTART_BCSTART")
159                 .WithFlag(15, out shorts.ReadyEnergyDetectStart, name: "READY_EDSTART")
160                 .WithFlag(16, out shorts.EnergyDetectEndDisable, name: "EDEND_DISABLE")
161                 .WithFlag(17, out shorts.CCAIdleStop, name: "CCAIDLE_STOP")
162                 .WithFlag(18, out shorts.TxReadyStart, name: "TXREADY_START")
163                 .WithFlag(19, out shorts.RxReadyStart, name: "RXREADY_START")
164                 .WithFlag(20, out shorts.PHYEndDisable, name: "PHYEND_DISABLE")
165                 .WithFlag(21, out shorts.PHYEndStart, name: "PHYEND_START")
166                 .WithReservedBits(22, 10)
167             ;
168 
169             RegistersCollection.AddRegister((long)Registers.InterruptEnable,
170                 interruptManager.GetInterruptEnableSetRegister<DoubleWordRegister>());
171 
172             RegistersCollection.AddRegister((long)Registers.InterruptDisable,
173                 interruptManager.GetInterruptEnableClearRegister<DoubleWordRegister>());
174 
175             Registers.CRCStatus.Define(this)
176                .WithFlag(0, name: "CRCSTATUS", valueProviderCallback: _ => true) // we assume here that CRCs are always ok
177                .WithReservedBits(1, 31)
178             ;
179 
180             Registers.PacketPointer.Define(this)
181                 .WithValueField(0, 32, out packetPointer, name: "PACKETPTR")
182             ;
183 
184             Registers.Frequency.Define(this, name: "FREQUENCY")
185                 .WithValueField(0, 7, out frequency, name: "FREQUENCY")
186                 .WithReservedBits(7, 1)
187                 .WithFlag(8, out frequencyMap, name: "MAP")
188                 .WithReservedBits(9, 23)
189             ;
190 
191             Registers.TxPower.Define(this, name: "TXPOWER")
192                 .WithValueField(0, 8, name: "TXPOWER") // just RW
193                 .WithReservedBits(8, 24)
194             ;
195 
196             Registers.Mode.Define(this, name: "MODE")
197                 .WithValueField(0, 4, name: "MODE") // just RW
198                 .WithReservedBits(4, 24)
199             ;
200 
201             Registers.PacketConfiguration0.Define(this, name: "PCNF0")
202                 .WithValueField(0, 4, out lengthFieldLength, name: "LFLEN")
203                 .WithReservedBits(4, 4)
204                 .WithValueField(8, 1, out s0Length, name: "S0LEN")
205                 .WithReservedBits(9, 7)
206                 .WithValueField(16, 4, out s1Length, name: "S1LEN")
207                 .WithFlag(20, out s1Include, name: "S1INCL")
208                 .WithReservedBits(21, 1)
209                 .WithValueField(22, 2, out codeIndicatorLength, name: "CILEN")
210                 .WithTag("PLEN", 24, 2)
211                 .WithFlag(26, out crcIncludedInLength, name: "CRCINC")
212                 .WithReservedBits(27, 2)
213                 .WithValueField(29, 2, out termLength, name: "TERMLEN")
214                 .WithReservedBits(31, 1)
215             ;
216 
217             Registers.PacketConfiguration1.Define(this, name: "PCNF1")
218                 .WithValueField(0, 8, out maxPacketLength, name: "MAXLEN")
219                 .WithValueField(8, 8, out staticLength, name: "STATLEN")
220                 .WithValueField(16, 3, out baseAddressLength, name: "BALEN")
221                 .WithReservedBits(19, 5)
222                 .WithTaggedFlag("ENDIAN", 24)
223                 .WithTaggedFlag("WHITEEN", 25)
224                 .WithReservedBits(26, 6)
225             ;
226 
227             Registers.BaseAddress0.Define(this)
228                 .WithValueField(0, 32, out baseAddress0, name: "BASE0")
229             ;
230 
231             Registers.BaseAddress1.Define(this)
232                 .WithValueField(0, 32, out baseAddress1, name: "BASE1")
233             ;
234 
235             Registers.Prefix0.Define(this, name: "PREFIX0")
236                 .WithValueFields(0, 8, 4, writeCallback: (i, _, value) => addressPrefixes[i] = (byte)value, name: "AP")
237             ;
238 
239             Registers.Prefix1.Define(this, name: "PREFIX1")
240                 .WithValueFields(0, 8, 4, writeCallback: (i, _, value) => addressPrefixes[4 + i] = (byte)value, name: "AP")
241             ;
242 
243             Registers.TxAddress.Define(this, name: "TXADDRESS")
244                 .WithValueField(0, 3, out txAddress, name: "TXADDRESS")
245                 .WithReservedBits(3, 29)
246             ;
247 
248             Registers.RxAddresses.Define(this, name: "RXADDRESSES")
249                 .WithFlags(0, 8, out rxAddressEnabled, name: "ADDR")
250             ;
251 
252             Registers.CRCConfiguration.Define(this, name: "CRCCNF")
253                 .WithValueField(0, 2, out crcLength, name: "LEN")
254                 .WithReservedBits(2, 6)
255                 .WithEnumField(8, 2, out crcSkipAddress, name: "SKIPADDR")
256                 .WithReservedBits(10, 21)
257             ;
258 
259             Registers.CRCPolynomial.Define(this, name: "CRCPOLY")
260                 .WithValueField(0, 24, out crcPolynomial, name: "CRCPOLY")
261                 .WithReservedBits(24, 8)
262             ;
263 
264             Registers.CRCInitialValue.Define(this, name: "CRCINIT")
265                 .WithValueField(0, 24, out crcInitialValue, name: "CRCINIT")
266                 .WithReservedBits(24, 8)
267             ;
268 
269             Registers.RSSISample.Define(this, name: "RSSISAMPLE")
270                 .WithValueField(0, 32, valueProviderCallback: _ => DefaultRSSISample, name: "RSSISAMPLE");
271             ;
272 
273             Registers.State.Define(this, name: "STATE")
274                 .WithEnumField<DoubleWordRegister, State>(0, 4, FieldMode.Read, valueProviderCallback: _ => radioState, name: "STATE")
275                 .WithReservedBits(4, 28)
276             ;
277 
278             Registers.BitCounterCompare.Define(this, name: "BCC")
279                 .WithValueField(0, 32, out bitCountCompare)
280             ;
281 
282             Registers.ModeConfiguration0.Define(this, 0x200)
283                 .WithTaggedFlag("RU", 0)
284                 .WithReservedBits(1, 7)
285                 .WithTag("DTX", 8, 2)
286                 .WithReservedBits(10, 22)
287             ;
288 
289             Registers.CCAControl.Define(this, 0x052D0000, name: "CCACTRL")
290                 .WithEnumField(0, 3, out ccaMode, name: "CCAMODE")
291                 .WithReservedBits(3, 5)
292                 .WithTag("CCAEDTHRES", 8, 8)
293                 .WithTag("CCACORRTHRES", 16, 8)
294                 .WithTag("CCACORRCNT", 24, 8)
295             ;
296 
297             Registers.PowerControl.Define(this, 1, name: "POWER")
298                 //TODO: radio should be disabled with powerOn == false
299                 .WithFlag(0, out powerOn, changeCallback: (_, value) => { if(!value) Reset(); }, name: "POWER")
300                 .WithReservedBits(1, 31)
301             ;
302         }
303 
LogUnhandledShort(IFlagRegisterField field, string shortName)304         private void LogUnhandledShort(IFlagRegisterField field, string shortName)
305         {
306             if(field.Value)
307             {
308                 this.Log(LogLevel.Error, $"Unhandled short {shortName}!");
309             }
310         }
311 
312         private void SetEvent(Events @event)
313         {
314             interruptManager.SetInterrupt(@event);
315             events[(int)@event].Value = true;
316             EventTriggered?.Invoke((uint)@event * 4 + 0x100);
317         }
318 
Disable()319         private void Disable()
320         {
321             radioState = State.Disabled;
322             SetEvent(Events.Disabled);
323             LogUnhandledShort(shorts.DisabledRSSIStop, nameof(shorts.DisabledRSSIStop));
324             LogUnhandledShort(shorts.DisabledRxEnable, nameof(shorts.DisabledRxEnable));
325             LogUnhandledShort(shorts.DisabledTxEnable, nameof(shorts.DisabledTxEnable));
326         }
327 
328         // These comments sum up some details gathered from the documentation.
329         //
330         // Packet:
331         // preamble, address (base + prefix), CI, TERM1, S0, LENGTH, S1, PAYLOAD, STATIC PAYLOAD (from STATLEN), CRC, TERM2
332 
333         // Packet data stored in RAM:
334         // S0, Length, S1, Payload
335 
336         // CRC:
337         // start with address, start with CI or start with L1
338 
339         // S0 + Length + S1 + payload <= 258 bytes
340 
341         // transmit sequence
342         // TXEn -> State.TxRampup -> Event.Ready -> State.TxIdle.  Start -> State.Tx. Sending data: P, A, --> Event.Address,  S0, L, S1, Payload, --> Event.Payload, CRC, --> Event.End (data finished) -> State.TxIdle.
343         // Disable -> State.TxDisable -> Event.Disabled
344 
345         // transmit sequence with shortcuts
346         // TXEn -> State.TxRampup -> Event.Ready shortcut to Start, Event.End shortcut to Disable
347 
348         // transmit multiple packets
349         // no shortcut end->disable. After Event.End, send start
350 
351         // receive sequence
352         // RXEn -> State.RxRampup -> Event.Ready -> State.RxIdle. Start->State.Rx. Receiving data: P, A --> Event.Address, s0, l, s1, payload  --> Event.Payload, crc -->Event.End (data finished) --> State.RxIdle
353         // Disable --> State.RxDisable -> Event.Disabled
354 
TxEnable()355         private void TxEnable()
356         {
357             radioState = State.TxIdle;
358             // we're skipping rampup, it's instant
359 
360             SetEvent(Events.Ready);
361             SetEvent(Events.TxReady);
362 
363             if(shorts.ReadyStart.Value || shorts.TxReadyStart.Value)
364             {
365                 Start();
366             }
367             LogUnhandledShort(shorts.ReadyEnergyDetectStart, nameof(shorts.ReadyEnergyDetectStart));
368         }
369 
RxEnable()370         private void RxEnable()
371         {
372             radioState = State.RxIdle;
373             // we're skipping rampup, it's instant
374 
375             SetEvent(Events.Ready);
376             SetEvent(Events.RxReady);
377 
378             if(shorts.ReadyStart.Value || shorts.RxReadyStart.Value)
379             {
380                 Start();
381             }
382             LogUnhandledShort(shorts.ReadyEnergyDetectStart, nameof(shorts.ReadyEnergyDetectStart));
383         }
384 
Start()385         private void Start()
386         {
387             // common task for both reception and transmission
388             if(radioState == State.TxIdle)
389             {
390                 SendPacket();
391             }
392             else if(radioState == State.RxIdle)
393             {
394                 // Can only receive one packet per enable
395                 if(rxBuffer.TryDequeue(out var pair))
396                 {
397                     ReceiveFrame(pair.Key, pair.Value);
398                 }
399             }
400             else
401             {
402                 this.Log(LogLevel.Error, "Triggered the Start task in an unexpected state {0}", radioState.ToString());
403             }
404         }
405 
HeaderLengthInAir()406         private int HeaderLengthInAir()
407         {
408            return (int)Math.Ceiling((s0Length.Value * 8 + lengthFieldLength.Value + s1Length.Value) / 8.0);
409         }
410 
HeaderLengthInRAM()411         private int HeaderLengthInRAM()
412         {
413            int ret = (int)s0Length.Value + (int)Math.Ceiling(lengthFieldLength.Value / 8.0) + (int)Math.Ceiling(s1Length.Value / 8.0);
414            if(s1Length.Value == 0 && s1Include.Value)
415            {
416               ret += 1;
417            }
418            return ret;
419         }
420 
SendPacket()421         private void SendPacket()
422         {
423             var dataAddress = (uint)packetPointer.Value;
424             var headerLengthInAir = HeaderLengthInAir();
425             var addressLength = (int)baseAddressLength.Value + 1;
426             var headerLengthInRAM = HeaderLengthInRAM();
427             if(headerLengthInAir != headerLengthInRAM)
428             {
429                 this.Log(LogLevel.Noisy, "Header length difference between onAir={0} and inRam={1}", headerLengthInAir, headerLengthInRAM);
430             }
431 
432             var data = new byte[addressLength + headerLengthInRAM + (uint)maxPacketLength.Value];
433             FillCurrentAddress(data, 0, (uint)txAddress.Value);
434 
435             sysbus.ReadBytes(dataAddress, headerLengthInRAM, data, addressLength);
436             this.Log(LogLevel.Noisy, "Header: {0} S0 {1} Length {2} S1 {3} s1inc {4}", Misc.PrettyPrintCollectionHex(data), s0Length.Value, lengthFieldLength.Value, s1Length.Value, s1Include.Value);
437             var payloadLength = data[addressLength + (uint)s0Length.Value];
438             if(payloadLength > maxPacketLength.Value)
439             {
440                 this.Log(LogLevel.Error, "Payload length ({0}) longer than the max packet length ({1}), trimming...", payloadLength, maxPacketLength.Value);
441                 payloadLength = (byte)maxPacketLength.Value;
442             }
443             sysbus.ReadBytes((ulong)(dataAddress + headerLengthInRAM), payloadLength, data, addressLength + headerLengthInAir);
444             this.Log(LogLevel.Noisy, "Data: {0} Maxlen {1} statlen {2}", Misc.PrettyPrintCollectionHex(data), maxPacketLength.Value, staticLength.Value);
445 
446             FrameSent?.Invoke(this, data);
447 
448             var crcLen = 4;
449             ScheduleRadioEvents((uint)(headerLengthInAir + payloadLength + crcLen));
450 
451             LogUnhandledShort(shorts.EndStart, nameof(shorts.EndStart)); // not sure how to support it. It's instant from our perspective.
452         }
453 
ScheduleRadioEvents(uint packetLen)454         private void ScheduleRadioEvents(uint packetLen)
455         {
456            var timeSource = machine.LocalTimeSource;
457            var now = timeSource.ElapsedVirtualTime;
458 
459            // @note  Transmit times assume 1M PHY. Low level BLE firmware
460            //        usually takes into account the active phy when calculating
461            //        timing delays, so we might need to do that.
462 
463            // Bit-counter
464            var bcMatchTime = now + TimeInterval.FromMicroseconds(bitCountCompare.Value);
465            var bcMatchTimeStamp = new TimeStamp(bcMatchTime, timeSource.Domain);
466 
467            // End event
468            var endTime = now + TimeInterval.FromMicroseconds((uint)(packetLen) * 8);
469            var endTimeStamp = new TimeStamp(endTime, timeSource.Domain);
470 
471            var disableTime = endTime + TimeInterval.FromMicroseconds(10);
472            var disableTimeStamp = new TimeStamp(disableTime, timeSource.Domain);
473 
474            // Address modelled as happening immediatley and serves as anchor
475            // point for other events. RIOT triggers IRQ from it.
476            SetEvent(Events.Address);
477 
478            // RSSI sample period is 0.25 us. Acceptable to model as happening
479            // immediatley upon start
480            if(shorts.AddressRSSIStart.Value)
481            {
482               SetEvent(Events.RSSIEnd);
483            }
484 
485            // Schedule a single bit-counter compare event not eariler than
486            // `bitCountCompare` microseconds from now.
487            // This is sufficient for BLE with RIOT stack, however it is possible to use bit
488            // counter to generate successive events, which this model will not
489            // support.
490            if(shorts.AddressBitCountStart.Value)
491            {
492               timeSource.ExecuteInSyncedState(_ =>
493               {
494                  SetEvent(Events.BitCountMatch);
495               }, bcMatchTimeStamp);
496            }
497 
498            // Schedule "end" events all at once, simulating the transmision time
499            // as 8 microseconds-per-byte. Timing distinction between events here doesn't
500            // seem important
501            timeSource.ExecuteInSyncedState(_ =>
502            {
503               SetEvent(Events.Payload);
504               SetEvent(Events.End);
505               SetEvent(Events.CRCOk);
506            }, endTimeStamp);
507 
508            // BLE stacks use disabled event as common processing trigger.
509            timeSource.ExecuteInSyncedState(_ =>
510            {
511               if(shorts.EndDisable.Value)
512               {
513                  Disable();
514               }
515            }, disableTimeStamp);
516         }
517 
FillCurrentAddress(byte[] data, int startIndex, uint logicalAddress)518         private void FillCurrentAddress(byte[] data, int startIndex, uint logicalAddress)
519         {
520             // based on 6.20.2 Address configuration
521             var baseAddress = (uint)(logicalAddress == 0 ? baseAddress0.Value : baseAddress1.Value);
522             var baseBytes = BitConverter.GetBytes(baseAddress);
523             var i = 0;
524             if(baseAddressLength.Value > 4)
525             {
526                 this.Log(LogLevel.Error, "Trying to fill the current address, but the base address length is too large ({0}). Limiting to 4.", baseAddressLength.Value);
527                 baseAddressLength.Value = 4;
528             }
529             for(var j = 4 - baseAddressLength.Value; j < 4; i++, j++) // we're not supporting BALEN > 4. I don't know how  should it work.
530             {
531                 data[startIndex + i] = baseBytes[j];
532             }
533             data[startIndex + i] = addressPrefixes[logicalAddress];
534         }
535 
536         private const int DefaultRSSISample = 10;
537 
538         private readonly ConcurrentQueue<KeyValuePair<byte[], IRadio>> rxBuffer;
539         private Shorts shorts;
540         private byte[] addressPrefixes;
541         private State radioState;
542         private InterruptManager<Events> interruptManager;
543 
544         private IFlagRegisterField[] events;
545         private IValueRegisterField packetPointer;
546         private IValueRegisterField frequency;
547         private IFlagRegisterField frequencyMap;
548         private IValueRegisterField lengthFieldLength;
549         private IValueRegisterField s0Length;
550         private IValueRegisterField s1Length;
551         private IFlagRegisterField s1Include;
552         private IValueRegisterField codeIndicatorLength;
553         private IFlagRegisterField crcIncludedInLength;
554         private IValueRegisterField termLength;
555         private IValueRegisterField maxPacketLength;
556         private IValueRegisterField staticLength;
557         private IValueRegisterField baseAddressLength;
558         private IValueRegisterField baseAddress0;
559         private IValueRegisterField baseAddress1;
560         private IValueRegisterField txAddress;
561         private IFlagRegisterField[] rxAddressEnabled;
562 
563         private IValueRegisterField bitCountCompare;
564 
565         private IValueRegisterField crcLength;
566         private IEnumRegisterField<CRCAddressHandling> crcSkipAddress;
567         private IValueRegisterField crcPolynomial;
568         private IValueRegisterField crcInitialValue;
569         private IEnumRegisterField<CCAMode> ccaMode;
570         private IFlagRegisterField powerOn;
571 
572         private readonly Dictionary<uint, int> bluetoothLEChannelMap = new Dictionary<uint, int>()
573         {
574            { 2402, 37 },
575            { 2404, 0 },
576            { 2406, 1 },
577            { 2408, 2 },
578            { 2410, 3 },
579            { 2412, 4 },
580            { 2414, 5 },
581            { 2416, 6 },
582            { 2418, 7 },
583            { 2420, 8 },
584            { 2422, 9 },
585            { 2424, 10 },
586            { 2426, 38 },
587            { 2428, 11 },
588            { 2430, 12 },
589            { 2432, 13 },
590            { 2434, 14 },
591            { 2436, 15 },
592            { 2438, 16 },
593            { 2440, 17 },
594            { 2442, 18 },
595            { 2444, 19 },
596            { 2446, 20 },
597            { 2448, 21 },
598            { 2450, 22 },
599            { 2452, 23 },
600            { 2454, 24 },
601            { 2456, 25 },
602            { 2458, 26 },
603            { 2460, 27 },
604            { 2462, 28 },
605            { 2464, 29 },
606            { 2466, 30 },
607            { 2468, 31 },
608            { 2470, 32 },
609            { 2472, 33 },
610            { 2474, 34 },
611            { 2476, 35 },
612            { 2478, 36 },
613            { 2480, 39 },
614         };
615 
616         private struct Shorts
617         {
618             public IFlagRegisterField ReadyStart;
619             public IFlagRegisterField EndDisable;
620             public IFlagRegisterField DisabledTxEnable;
621             public IFlagRegisterField DisabledRxEnable;
622             public IFlagRegisterField AddressRSSIStart;
623             public IFlagRegisterField EndStart;
624             public IFlagRegisterField AddressBitCountStart;
625             public IFlagRegisterField DisabledRSSIStop;
626             public IFlagRegisterField RxReadyCCAStart;
627             public IFlagRegisterField CCAIdleTxEnable;
628             public IFlagRegisterField CCABusyDisable;
629             public IFlagRegisterField FrameStartBitCountStart;
630             public IFlagRegisterField ReadyEnergyDetectStart;
631             public IFlagRegisterField EnergyDetectEndDisable;
632             public IFlagRegisterField CCAIdleStop;
633             public IFlagRegisterField TxReadyStart;
634             public IFlagRegisterField RxReadyStart;
635             public IFlagRegisterField PHYEndDisable;
636             public IFlagRegisterField PHYEndStart;
637         }
638 
639         private enum CRCAddressHandling
640         {
641             Include = 0,
642             Skip = 1,
643             IEEE802154 = 2
644         }
645 
646         private enum State
647         {
648             Disabled = 0,
649             RxRampup = 1,
650             RxIdle = 2,
651             Rx = 3,
652             RxDisable = 4,
653             TxRampup = 9,
654             TxIdle = 10,
655             Tx = 11,
656             TxDisable = 12,
657         }
658 
659         private enum Events
660         {
661             Ready = 0,
662             Address = 1,
663             Payload = 2,
664             End = 3,
665             Disabled = 4,
666             DeviceAddressMatch = 5,
667             DeviceAddressMiss = 6,
668             RSSIEnd = 7,
669             BitCountMatch = 10,
670             CRCOk = 12,
671             CRCError = 13,
672             FrameStart = 14,
673             EnergyDetectEnd = 15,
674             EnergyDetectStopped = 16,
675             CCAIdle = 17,
676             CCABusy = 18,
677             CCAStopped = 19,
678             RateBoost = 20,
679             TxReady = 21,
680             RxReady = 22,
681             MACHeaderMatch = 23,
682             Sync = 26,
683             PHYEnd = 27
684         }
685 
686         private enum CCAMode
687         {
688             EdMode,
689             CarrierMode,
690             CarrierAndEdMode,
691             CarrierOrEdMode,
692             EdMoteTest1
693         }
694 
695         private enum Registers
696         {
697             TxEnable = 0x000,
698             RxEnable = 0x004,
699             Start = 0x008,
700             Stop = 0x00C,
701             Disable = 0x010,
702             RSSIStart = 0x014,
703             RSSIStop = 0x018,
704             BitCounterStart = 0x01C,
705             BitCounterStop = 0x020,
706             EnergyDetectStart = 0x024,
707             EnergyDetectStop = 0x028,
708             CCAStart = 0x02C,
709             CCAStop = 0x030,
710             Ready = 0x100,
711             AddressSentOrReceived = 0x104,
712             PayloadSentOrReceived = 0x108,
713             PacketSentOrReceived = 0x10C,
714             RadioDisabled = 0x110,
715             DeviceMatch = 0x114,
716             DeviceMiss = 0x118,
717             RSSIEnd = 0x11C,
718             BitCounterMatch = 0x128,
719             CRCOk = 0x130,
720             CRCError = 0x134,
721             FrameStartReceived = 0x138,
722             EnergyDetectEnd = 0x13C,
723             EnergyDetectStopped = 0x140,
724             CCAIdle = 0x144,
725             CCABusy = 0x148,
726             CCAStopped = 0x14C,
727             RateBoost = 0x150,
728             TxReady = 0x154,
729             RxReady = 0x158,
730             MACHeaderMatch = 0x15C,
731             Sync = 0x168,
732             PHYEnd = 0x16C,
733             Shorts = 0x200,
734             InterruptEnable = 0x304,
735             InterruptDisable = 0x308,
736             CRCStatus = 0x400,
737             RxMatch = 0x408,
738             RxCRC = 0x40C,
739             DeviceAddressMatchIndex = 0x410,
740             PayloadStatus = 0x414,
741             PacketPointer = 0x504,
742             Frequency = 0x508,
743             TxPower = 0x50C,
744             Mode = 0x510,
745             PacketConfiguration0 = 0x514,
746             PacketConfiguration1 = 0x518,
747             BaseAddress0 = 0x51C,
748             BaseAddress1 = 0x520,
749             Prefix0 = 0x524,
750             Prefix1 = 0x528,
751             TxAddress = 0x52C,
752             RxAddresses = 0x530,
753             CRCConfiguration = 0x534,
754             CRCPolynomial = 0x538,
755             CRCInitialValue = 0x53C,
756             InterframeSpacing = 0x544,
757             RSSISample = 0x548,
758             State = 0x550,
759             DataWhiteningInitialValue = 0x554,
760             BitCounterCompare = 0x560,
761             DeviceAddressBaseSegment0 = 0x600,
762             DeviceAddressBaseSegment1 = 0x604,
763             DeviceAddressBaseSegment2 = 0x608,
764             DeviceAddressBaseSegment3 = 0x60C,
765             DeviceAddressBaseSegment4 = 0x610,
766             DeviceAddressBaseSegment5 = 0x614,
767             DeviceAddressBaseSegment6 = 0x618,
768             DeviceAddressBaseSegment7 = 0x61C,
769             DeviceAddressPrefix0 = 0x620,
770             DeviceAddressPrefix1 = 0x624,
771             DeviceAddressPrefix2 = 0x628,
772             DeviceAddressPrefix3 = 0x62C,
773             DeviceAddressPrefix4 = 0x630,
774             DeviceAddressPrefix5 = 0x634,
775             DeviceAddressPrefix6 = 0x638,
776             DeviceAddressPrefix7 = 0x63C,
777             DeviceAddressMatchConfiguration = 0x640,
778             SearchPatternConfiguration = 0x644,
779             PatternMask = 0x648,
780             ModeConfiguration0 = 0x650,
781             StartOfFrameDelimiter = 0x660,
782             EnergyDetectLoopCount = 0x664,
783             EnergyDetectLevel = 0x668,
784             CCAControl = 0x66C,
785             PowerControl = 0xFFC
786         }
787     }
788 }
789