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 Antmicro.Renode.Peripherals.SPI; 9 using Antmicro.Renode.Logging; 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Peripherals.Wireless.IEEE802_15_4; 12 using System.Linq; 13 14 namespace Antmicro.Renode.Peripherals.Wireless 15 { 16 public class AT86RF233 : ISPIPeripheral, IRadio, IGPIOReceiver 17 { AT86RF233()18 public AT86RF233() 19 { 20 IRQ = new GPIO(); 21 Reset(); 22 } 23 ReceiveFrame(byte[] frame, IRadio sender)24 public void ReceiveFrame(byte[] frame, IRadio sender) 25 { 26 this.DebugLog("Frame of length {0} received.", frame.Length); 27 if(frame.Length == 0) 28 { 29 // according to the documentation: 30 // Received frames with a frame length field set to zero (invalid PHR) are not signaled to the microcontroller. 31 this.DebugLog("Ignoring an empty frame."); 32 return; 33 } 34 35 if(operatingMode == OperatingMode.RxAackOn || operatingMode == OperatingMode.RxOn) 36 { 37 HandleFrame(frame); 38 } 39 else 40 { 41 deferredFrameBuffer = frame; 42 this.DebugLog("Radio is not listening right now - this frame is being deffered."); 43 } 44 } 45 Reset()46 public void Reset() 47 { 48 autoCrc = true; 49 accessMode = AccessMode.Command; 50 frameBuffer = new byte[0]; 51 deferredFrameBuffer = null; 52 } 53 OnGPIO(int number, bool value)54 public void OnGPIO(int number, bool value) 55 { 56 if(number != 0) 57 { 58 this.Log(LogLevel.Warning, "Unexpected GPIO: {0}", number); 59 return; 60 } 61 62 this.DebugLog("Chip select set to: {0}", value); 63 if(value && accessMode == AccessMode.FrameBufferAccess && accessType == AccessType.ReadAccess) 64 { 65 accessMode = AccessMode.Command; 66 } 67 } 68 Transmit(byte data)69 public byte Transmit(byte data) 70 { 71 this.DebugLog("Byte received: 0x{0:X}", data); 72 switch(accessMode) 73 { 74 case AccessMode.Command: 75 HandleCommandByte(data); 76 break; 77 case AccessMode.RegisterAccess: 78 var result = (byte)0; 79 if(accessType == AccessType.ReadAccess) 80 { 81 result = HandleRegisterRead((Registers)context); 82 } 83 else 84 { 85 HandleRegisterWrite((Registers)context, data); 86 } 87 accessMode = AccessMode.Command; 88 return result; 89 case AccessMode.FrameBufferAccess: 90 if(accessType == AccessType.ReadAccess) 91 { 92 return HandleFrameBufferRead(context++); 93 } 94 else 95 { 96 HandleFrameBufferWrite(context++, data); 97 } 98 break; 99 default: 100 this.Log(LogLevel.Warning, "Unsupported access mode: {0}", accessMode); 101 break; 102 } 103 return 0; 104 } 105 FinishTransmission()106 public void FinishTransmission() 107 { 108 } 109 110 public int Channel { get; set; } 111 112 public event Action<IRadio, byte[]> FrameSent; 113 114 public GPIO IRQ { get; private set; } 115 HandleCommandByte(byte data)116 private void HandleCommandByte(byte data) 117 { 118 // according to the documentation 'data' byte should be interpreted as follows: 119 // 1 0 A A A A A A - Register read from address A A A A A A 120 // 1 1 A A A A A A - Register write to address A A A A A A 121 // 0 0 1 x x x x x - Frame buffer read 122 // 0 1 0 x x x x x - Frame buffer write 123 // 0 0 0 x x x x x - SRAM read 124 // 0 1 0 x x x x x - SRAM write 125 126 if((data & (1 << 7)) == 0) 127 { 128 accessMode = ((data >> 5) & 1) == 0 ? AccessMode.SramAccess : AccessMode.FrameBufferAccess; 129 } 130 else 131 { 132 accessMode = AccessMode.RegisterAccess; 133 } 134 accessType = (data & (1 << 6)) == 0 ? AccessType.ReadAccess : AccessType.WriteAccess; 135 if(accessMode == AccessMode.RegisterAccess) 136 { 137 context = (byte)(data & 0x3F); 138 this.DebugLog("Register 0x{0:X} {1} request", context, accessType == AccessType.ReadAccess ? "read" : "write"); 139 } 140 else 141 { 142 context = -1; 143 this.DebugLog("Command received: {0} {1}", accessMode.ToString(), accessType.ToString()); 144 } 145 } 146 HandleFrame(byte[] frame)147 private void HandleFrame(byte[] frame) 148 { 149 // fcs (crc) check 150 if(frame.Length >= 2) 151 { 152 var crc = Frame.CalculateCRC(frame.Take(frame.Length - 2)); 153 if(frame[frame.Length - 2] != crc[0] || frame[frame.Length - 1] != crc[1]) 154 { 155 this.Log(LogLevel.Warning, "A frame with wrong CRC received - dropping it."); 156 return; 157 } 158 } 159 else 160 { 161 this.Log(LogLevel.Warning, "Short frame (length {0}) received - CRC is not checked.", frame.Length); 162 } 163 164 if(operatingMode == OperatingMode.RxAackOn && Frame.IsAcknowledgeRequested(frame)) 165 { 166 var frameSent = FrameSent; 167 if(frameSent != null) 168 { 169 var ackFrame = Frame.CreateAckForFrame(frame); 170 this.DebugLog("Sending automatic ACK for frame sequence number: {0}", ackFrame.DataSequenceNumber); 171 frameSent(this, ackFrame.Bytes); 172 } 173 } 174 175 frameBuffer = frame; 176 this.DebugLog("Setting IRQ"); 177 IRQ.Set(); 178 } 179 HandleRegisterRead(Registers register)180 private byte HandleRegisterRead(Registers register) 181 { 182 this.Log(LogLevel.Noisy, "Reading register {0}.", register); 183 switch(register) 184 { 185 case Registers.TrxStatus: 186 return (byte)((deferredFrameBuffer == null ? 0xC0 : 0x80) + (byte)operatingMode); // CCA_DONE + CCA_STATUS 187 case Registers.TrxState: 188 return (byte)operatingMode; 189 case Registers.VersionNum: 190 return 0x1; // Revision A 191 case Registers.ManId0: 192 return 0x1F; // Atmel JEDEC manufacturer ID 193 case Registers.ManId1: 194 return 0x0; // Atmel JEDEC manufacturer ID 195 case Registers.TrxCtrl0: 196 return 0x9; // CLKDM_SHA_SEL + CLKM_CTRL (1Mhz) 197 case Registers.TrxCtrl2: 198 return 0x20; // OQPSK_SCRAM_EN 199 case Registers.IrqStatus: 200 IRQ.Unset(); 201 // TODO: this probably should not be hardcoded 202 return 0x8 + 0x20; // TRX_END + AMI 203 case Registers.PhyCcCca: 204 return (byte)(0x20 + Channel); // CCA_MODE + Channel 205 case Registers.PhyEdLevel: 206 return 0x53; // maximum ED level value 207 case Registers.PhyRssi: 208 return 0x9c; // RxCrcValid + Maximum RSSI value 209 default: 210 this.Log(LogLevel.Warning, "Read from unexpected register: 0x{0:X}", context); 211 return 0; 212 } 213 } 214 HandleRegisterWrite(Registers register, byte data)215 private void HandleRegisterWrite(Registers register, byte data) 216 { 217 this.Log(LogLevel.Noisy, "Writing register {0} with data {1:X}.", register, data); 218 switch(register) 219 { 220 case Registers.TrxState: 221 data &= 0x1f; 222 if(!Enum.IsDefined(typeof(OperatingMode), data)) 223 { 224 this.Log(LogLevel.Warning, "Unknown mode: 0x{0:1}", data); 225 return; 226 } 227 operatingMode = (OperatingMode)data; 228 this.Log(LogLevel.Info, "Entering {0} mode", operatingMode.ToString()); 229 if(operatingMode == OperatingMode.ForceTrxOff) 230 { 231 operatingMode = OperatingMode.TrxOff; 232 this.Log(LogLevel.Info, "Entering {0} mode", operatingMode.ToString()); 233 } 234 if((operatingMode == OperatingMode.RxOn || operatingMode == OperatingMode.RxAackOn) && deferredFrameBuffer != null) 235 { 236 HandleFrame(deferredFrameBuffer); 237 deferredFrameBuffer = null; 238 } 239 break; 240 case Registers.PhyCcCca: 241 Channel = data & 0x1F; 242 this.Log(LogLevel.Info, "Setting channel {0}", Channel); 243 break; 244 case Registers.TrxCtrl1: 245 autoCrc = ((data & 0x20) != 0); 246 this.Log(LogLevel.Info, "Auto CRC turned {0}", autoCrc ? "on" : "off"); 247 break; 248 default: 249 this.Log(LogLevel.Warning, "Write value 0x{0:X} to unexpected register: 0x{1:X}", data, context); 250 break; 251 } 252 } 253 HandleFrameBufferRead(int index)254 private byte HandleFrameBufferRead(int index) 255 { 256 if(index == -1) 257 { 258 // this means we have to send PHR byte indicating frame length 259 return (byte)frameBuffer.Length; 260 } 261 if(context < frameBuffer.Length) 262 { 263 return frameBuffer[index]; 264 } 265 if(index == frameBuffer.Length) 266 { 267 // send LQI 268 return 0xFF; // the best Link Quality Indication 269 } 270 if(index == frameBuffer.Length + 1) 271 { 272 // send ED 273 return 0x53; // maximum Energy Detection level 274 } 275 if(index == frameBuffer.Length + 2) 276 { 277 accessMode = AccessMode.Command; 278 // send RX_STATUS 279 return 0x80; // CRC ok 280 } 281 return 0; 282 } 283 HandleFrameBufferWrite(int index, byte data)284 private void HandleFrameBufferWrite(int index, byte data) 285 { 286 if(index == -1) 287 { 288 // this is PHR 289 frameBuffer = new byte[data]; 290 } 291 else 292 { 293 frameBuffer[index] = data; 294 } 295 if(index == frameBuffer.Length - 1) 296 { 297 SendPacket(); 298 accessMode = AccessMode.Command; 299 } 300 } 301 SendPacket()302 private void SendPacket() 303 { 304 var frameSent = FrameSent; 305 if(frameSent == null) 306 { 307 this.NoisyLog("Could not sent packet as there is no frame handler attached."); 308 return; 309 } 310 311 if(autoCrc) 312 { 313 if(frameBuffer.Length >= 2) 314 { 315 // replace buffer's last two bytes with calculated CRC 316 var frameDataLenght = frameBuffer.Length - 2; 317 var crc = Frame.CalculateCRC(frameBuffer.Take(frameDataLenght)); 318 Array.Copy(crc, 0, frameBuffer, frameDataLenght, 2); 319 } 320 else 321 { 322 this.Log(LogLevel.Warning, "Sending short frame (length {0}) - CRC is not calculated.", frameBuffer.Length); 323 } 324 } 325 326 frameSent(this, frameBuffer); 327 this.DebugLog("Frame of length {0} sent.", frameBuffer.Length); 328 } 329 330 private OperatingMode operatingMode; 331 private bool autoCrc; 332 private int context; 333 private AccessMode accessMode; 334 private AccessType accessType; 335 private byte[] frameBuffer; 336 // this is used to hold the last frame received when radio was in TrxOff mode; 337 // conserving energy can lead to disabling radio (going into TrxOff mode) and 338 // turning it on only periodically; during those short period network acticity 339 // is assessed by monitoring channel state (CCA); in the emulator network communication 340 // takes no time so it is problematic to decide if CCA should return true or false; 341 // here comes the trick: if there was any frame received during TrxOff mode CCA 342 // returns channel busy state and only when the radio switches to RxOn mode the frame 343 // is actualy received 344 private byte[] deferredFrameBuffer; 345 346 private enum OperatingMode : byte 347 { 348 ForceTrxOff = 0x3, 349 RxOn = 0x6, 350 TrxOff = 0x8, 351 PllOn = 0x9, 352 RxAackOn = 0x16, 353 TxAretOn = 0x19 354 } 355 356 private enum AccessMode 357 { 358 Command, 359 RegisterAccess, 360 FrameBufferAccess, 361 SramAccess 362 } 363 364 private enum AccessType 365 { 366 ReadAccess, 367 WriteAccess 368 } 369 370 private enum Registers 371 { 372 TrxStatus = 0x1, 373 TrxState = 0x2, 374 TrxCtrl0 = 0x3, 375 TrxCtrl1 = 0x4, 376 PhyRssi = 0x6, 377 PhyEdLevel = 0x7, 378 PhyCcCca = 0x8, 379 TrxCtrl2 = 0xC, 380 IrqStatus = 0xF, 381 VersionNum = 0x1D, 382 ManId0 = 0x1E, 383 ManId1 = 0x1F 384 } 385 } 386 } 387