1 // 2 // Copyright (c) 2010-2023 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 8 using System.Linq; 9 using System.Collections.Generic; 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Core.Structure; 12 using Antmicro.Renode.Core.Structure.Registers; 13 using Antmicro.Renode.Logging; 14 using Antmicro.Renode.Peripherals.Bus; 15 16 namespace Antmicro.Renode.Peripherals.SPI 17 { 18 public class NRF52840_SPI : NullRegistrationPointPeripheralContainer<ISPIPeripheral>, IDoubleWordPeripheral, IProvidesRegisterCollection<DoubleWordRegisterCollection>, IKnownSize 19 { NRF52840_SPI(IMachine machine, bool easyDMA = false)20 public NRF52840_SPI(IMachine machine, bool easyDMA = false) : base(machine) 21 { 22 this.machine = machine; 23 sysbus = machine.GetSystemBus(this); 24 this.easyDMA = easyDMA; 25 26 IRQ = new GPIO(); 27 28 receiveFifo = new Queue<byte>(); 29 RegistersCollection = new DoubleWordRegisterCollection(this); 30 DefineRegisters(); 31 Reset(); 32 } 33 Reset()34 public override void Reset() 35 { 36 receiveFifo.Clear(); 37 enabled = false; 38 RegistersCollection.Reset(); 39 UpdateInterrupts(); 40 } 41 ReadDoubleWord(long offset)42 public uint ReadDoubleWord(long offset) 43 { 44 return RegistersCollection.Read(offset); 45 } 46 WriteDoubleWord(long offset, uint value)47 public void WriteDoubleWord(long offset, uint value) 48 { 49 RegistersCollection.Write(offset, value); 50 } 51 52 public GPIO IRQ { get; } 53 54 public DoubleWordRegisterCollection RegistersCollection { get; } 55 56 public long Size => 0x1000; 57 DefineRegisters()58 private void DefineRegisters() 59 { 60 Registers.PendingInterrupt.Define(this) 61 .WithFlag(0, out readyPending, name: "EVENTS_READY") 62 .WithReservedBits(1, 31) 63 .WithWriteCallback((_, __) => UpdateInterrupts()) 64 ; 65 66 if(easyDMA) 67 { 68 Registers.EnableInterrupt.Define(this) 69 .WithReservedBits(0, 1) 70 .WithTaggedFlag("STOPPED", 1) 71 .WithReservedBits(2, 2) 72 .WithFlag(4, out endRxEnabled, FieldMode.Read | FieldMode.Set, name: "ENDRX") 73 .WithReservedBits(5, 1) 74 .WithFlag(6, out endEnabled, FieldMode.Read | FieldMode.Set, name: "END") 75 .WithReservedBits(7, 1) 76 .WithFlag(8, out endTxEnabled, FieldMode.Read | FieldMode.Set, name: "ENDTX") 77 .WithReservedBits(9, 10) 78 .WithFlag(19, out startedEnabled, FieldMode.Read | FieldMode.Set, name: "STARTED") 79 .WithReservedBits(20, 12) 80 .WithWriteCallback((_, __) => UpdateInterrupts()) 81 ; 82 83 Registers.DisableInterrupt.Define(this) 84 .WithReservedBits(0, 1) 85 .WithTaggedFlag("STOPPED", 1) 86 .WithReservedBits(2, 2) 87 .WithFlag(4, name: "ENDRX", 88 valueProviderCallback: _ => endRxEnabled.Value, 89 writeCallback: (_, val) => { if(val) endRxEnabled.Value = false; }) 90 .WithReservedBits(5, 1) 91 .WithFlag(6, name: "END", 92 valueProviderCallback: _ => endEnabled.Value, 93 writeCallback: (_, val) => { if(val) endEnabled.Value = false; }) 94 .WithReservedBits(7, 1) 95 .WithFlag(8, name: "ENDTX", 96 valueProviderCallback: _ => endTxEnabled.Value, 97 writeCallback: (_, val) => { if(val) endTxEnabled.Value = false; }) 98 .WithReservedBits(9, 10) 99 .WithFlag(19, name: "STARTED", 100 valueProviderCallback: _ => startedEnabled.Value, 101 writeCallback: (_, val) => { if(val) startedEnabled.Value = false; }) 102 .WithReservedBits(20, 12) 103 .WithWriteCallback((_, __) => UpdateInterrupts()) 104 ; 105 106 Registers.TxListType.Define(this) 107 .WithTaggedFlag("LIST - List type", 0) 108 .WithReservedBits(1, 31) 109 ; 110 111 Registers.RxListType.Define(this) 112 .WithTaggedFlag("LIST - List type", 0) 113 .WithReservedBits(1, 31) 114 ; 115 } 116 else 117 { 118 Registers.EnableInterrupt.Define(this) 119 .WithReservedBits(0, 2) 120 .WithFlag(2, out readyEnabled, FieldMode.Read | FieldMode.Set, name: "READY") 121 .WithReservedBits(3, 3) 122 .WithFlag(6, out endEnabled, FieldMode.Read | FieldMode.Set, name: "END") 123 .WithReservedBits(7, 25) 124 .WithWriteCallback((_, __) => UpdateInterrupts()) 125 ; 126 127 Registers.DisableInterrupt.Define(this) 128 .WithReservedBits(0, 2) 129 .WithFlag(2, name: "READY", 130 valueProviderCallback: _ => readyEnabled.Value, 131 writeCallback: (_, val) => { if(val) readyEnabled.Value = false; }) 132 .WithReservedBits(3, 3) 133 .WithFlag(6, name: "END", 134 writeCallback: (_, val) => { if(val) endEnabled.Value = false; }) 135 .WithReservedBits(7, 25) 136 .WithWriteCallback((_, __) => UpdateInterrupts()) 137 ; 138 } 139 140 Registers.Enable.Define(this) 141 .WithValueField(0, 4, 142 valueProviderCallback: _ => enabled ? 1 : 0u, 143 writeCallback: (_, val) => 144 { 145 switch(val) 146 { 147 case 0: 148 // disabled 149 enabled = false; 150 break; 151 152 case 1: 153 // enabled, standard mode 154 if(!easyDMA) 155 { 156 enabled = true; 157 } 158 break; 159 160 case 7: 161 // enabled, easyDMA mode 162 if(easyDMA) 163 { 164 enabled = true; 165 } 166 break; 167 168 default: 169 this.Log(LogLevel.Warning, "Unhandled enable value: 0x{0:X}", val); 170 return; 171 } 172 173 UpdateInterrupts(); 174 }) 175 .WithReservedBits(4, 28) 176 ; 177 178 Registers.TransmitBuffer.Define(this) 179 // the documentation says this field is readable, so we use the automatic 180 // underlying backing field to return the previously written value 181 .WithValueField(0, 8, name: "TXD", writeCallback: (_, val) => SendData((byte)val)) 182 .WithReservedBits(8, 24) 183 ; 184 185 Registers.ReceiveBuffer.Define(this) 186 .WithValueField(0, 8, FieldMode.Read, name: "RXD", 187 valueProviderCallback: _ => 188 { 189 if(receiveFifo.Count == 0) 190 { 191 this.Log(LogLevel.Warning, "Tried to read from an empty buffer"); 192 return 0; 193 } 194 195 lock(receiveFifo) 196 { 197 var result = receiveFifo.Dequeue(); 198 199 // some new byte moved to the head 200 // of the queue - let's generate the 201 // READY event 202 if(receiveFifo.Count > 0) 203 { 204 readyPending.Value = true; 205 UpdateInterrupts(); 206 } 207 return result; 208 } 209 }) 210 .WithReservedBits(8, 24) 211 ; 212 213 if(easyDMA) 214 { 215 Registers.TasksStart.Define(this) 216 .WithFlag(0, FieldMode.Write, name: "TASKS_START", writeCallback: (_, val) => 217 { 218 if(val) 219 { 220 machine.LocalTimeSource.ExecuteInNearestSyncedState(__ => ExecuteTransaction()); 221 } 222 }) 223 .WithReservedBits(1, 31) 224 ; 225 226 Registers.EventsStarted.Define(this) 227 .WithFlag(0, out startedPending, name: "EVENTS_STARTED") 228 .WithReservedBits(1, 31) 229 .WithWriteCallback((_, __) => UpdateInterrupts()) 230 ; 231 232 Registers.EventsEnd.Define(this) 233 .WithFlag(0, out endPending, name: "EVENTS_END") 234 .WithReservedBits(1, 31) 235 .WithWriteCallback((_, __) => UpdateInterrupts()) 236 ; 237 238 Registers.EventsEndTx.Define(this) 239 .WithFlag(0, out endTxPending, name: "EVENTS_ENDTX") 240 .WithReservedBits(1, 31) 241 .WithWriteCallback((_, __) => UpdateInterrupts()) 242 ; 243 244 Registers.EventsEndRx.Define(this) 245 .WithFlag(0, out endRxPending, name: "EVENTS_ENDRX") 246 .WithReservedBits(1, 31) 247 .WithWriteCallback((_, __) => UpdateInterrupts()) 248 ; 249 250 Registers.RxDataPointer.Define(this) 251 .WithValueField(0, 32, out rxDataPointer, name: "PTR") 252 ; 253 254 Registers.RxMaxDataCount.Define(this) 255 .WithValueField(0, 16, out rxMaxDataCount, name: "MAXCNT") 256 .WithReservedBits(16, 16) 257 ; 258 259 Registers.RxTransferredDataAmount.Define(this) 260 .WithValueField(0, 16, out rxTransferredDataAmount, FieldMode.Read, name: "AMOUNT") 261 .WithReservedBits(16, 16) 262 ; 263 264 Registers.TxDataPointer.Define(this) 265 .WithValueField(0, 32, out txDataPointer, name: "PTR") 266 ; 267 268 Registers.TxMaxDataCount.Define(this) 269 .WithValueField(0, 16, out txMaxDataCount, name: "MAXCNT") 270 .WithReservedBits(16, 16) 271 ; 272 273 Registers.TxTransferredDataAmount.Define(this) 274 .WithValueField(0, 16, out txTransferredDataAmount, FieldMode.Read, name: "AMOUNT") 275 .WithReservedBits(16, 16) 276 ; 277 278 Registers.ORC.Define(this) 279 .WithValueField(0, 8, out orcByte, name: "ORC") 280 .WithReservedBits(8, 24) 281 ; 282 } 283 } 284 ExecuteTransaction()285 private void ExecuteTransaction() 286 { 287 var receivedBytes = new byte[rxMaxDataCount.Value]; 288 289 startedPending.Value = true; 290 291 if(RegisteredPeripheral == null) 292 { 293 this.Log(LogLevel.Warning, "Issued a transaction, but no device is connected - returning dummy bytes"); 294 295 // in case there is no target device `receivedBytes` contain just zeros in our simulation 296 // (on a real HW that could be any nondeterministic value) 297 } 298 else 299 { 300 this.Log(LogLevel.Debug, "Starting SPI transaction using easyDMA interface"); 301 302 var bytesToSend = sysbus.ReadBytes(txDataPointer.Value, (int)txMaxDataCount.Value); 303 if(rxMaxDataCount.Value > txMaxDataCount.Value) 304 { 305 // fill the rest of bytes to transmit with the ORC byte 306 bytesToSend = bytesToSend.Concat(Enumerable.Repeat((byte)orcByte.Value, (int)(rxMaxDataCount.Value - txMaxDataCount.Value))).ToArray(); 307 } 308 309 var counter = 0; 310 foreach(var b in bytesToSend) 311 { 312 var result = RegisteredPeripheral.Transmit(b); 313 if(counter < receivedBytes.Length) 314 { 315 receivedBytes[counter++] = result; 316 } 317 // bytes over rxMaxDataCount are being discarded 318 } 319 } 320 321 sysbus.WriteBytes(receivedBytes, rxDataPointer.Value); 322 323 endTxPending.Value = true; 324 endRxPending.Value = true; 325 endPending.Value = true; 326 UpdateInterrupts(); 327 } 328 SendData(byte b)329 private void SendData(byte b) 330 { 331 if(!enabled) 332 { 333 this.Log(LogLevel.Warning, "Trying to send data, but the controller is disabled"); 334 return; 335 } 336 337 if(RegisteredPeripheral == null) 338 { 339 this.Log(LogLevel.Warning, "No device connected"); 340 return; 341 } 342 343 if(receiveFifo.Count == ReceiveBufferSize) 344 { 345 this.Log(LogLevel.Warning, "Buffers full, ignoring data"); 346 return; 347 } 348 349 // there is no need to queue transmitted bytes - let's send them right away 350 var result = RegisteredPeripheral.Transmit(b); 351 lock(receiveFifo) 352 { 353 receiveFifo.Enqueue(result); 354 355 // the READY event is generated 356 // only when the head 357 // of the queue changes 358 if(receiveFifo.Count == 1) 359 { 360 readyPending.Value = true; 361 UpdateInterrupts(); 362 } 363 } 364 } 365 366 // RXD is double buffered 367 private const int ReceiveBufferSize = 2; 368 UpdateInterrupts()369 private void UpdateInterrupts() 370 { 371 var status = false; 372 373 if(easyDMA) 374 { 375 status |= startedPending.Value && startedEnabled.Value; 376 status |= endPending.Value && endEnabled.Value; 377 status |= endRxPending.Value && endRxEnabled.Value; 378 status |= endTxPending.Value && endTxEnabled.Value; 379 } 380 else 381 { 382 status |= readyEnabled.Value && readyPending.Value; 383 } 384 status &= enabled; 385 386 this.Log(LogLevel.Noisy, "Setting IRQ to {0}", status); 387 IRQ.Set(status); 388 } 389 390 private IFlagRegisterField startedPending; 391 private IFlagRegisterField startedEnabled; 392 393 private IFlagRegisterField readyPending; 394 private IFlagRegisterField readyEnabled; 395 396 private IFlagRegisterField endEnabled; 397 private IFlagRegisterField endPending; 398 399 private IFlagRegisterField endTxEnabled; 400 private IFlagRegisterField endTxPending; 401 402 private IFlagRegisterField endRxEnabled; 403 private IFlagRegisterField endRxPending; 404 405 private IValueRegisterField txDataPointer; 406 private IValueRegisterField rxDataPointer; 407 private IValueRegisterField txMaxDataCount; 408 private IValueRegisterField rxMaxDataCount; 409 private IValueRegisterField txTransferredDataAmount; 410 private IValueRegisterField rxTransferredDataAmount; 411 412 private IValueRegisterField orcByte; 413 414 private bool enabled; 415 416 private readonly Queue<byte> receiveFifo; 417 private readonly IMachine machine; 418 private readonly IBusController sysbus; 419 private readonly bool easyDMA; 420 421 private enum Registers 422 { 423 TasksStart = 0x10, 424 PendingInterrupt = 0x108, 425 EventsEndRx = 0x110, 426 EventsEnd = 0x118, 427 EventsEndTx = 0x120, 428 EventsStarted = 0x14C, 429 EnableInterrupt = 0x304, 430 DisableInterrupt = 0x308, 431 Enable = 0x500, 432 PinSelectSCK = 0x508, 433 PinSelectMOSI = 0x50C, 434 PinSelectMISO = 0x510, 435 ReceiveBuffer = 0x518, 436 TransmitBuffer = 0x51C, 437 Frequency = 0x524, 438 RxDataPointer = 0x534, 439 RxMaxDataCount = 0x538, 440 RxTransferredDataAmount = 0x53C, 441 RxListType = 0x540, 442 TxDataPointer = 0x544, 443 TxMaxDataCount = 0x548, 444 TxTransferredDataAmount = 0x54C, 445 TxListType = 0x550, 446 Configuration = 0x554, 447 ORC = 0x5C0, 448 } 449 } 450 } 451