1 // 2 // Copyright (c) 2010-2024 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.Generic; 9 using System.Threading; 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 using Antmicro.Renode.Time; 16 using Antmicro.Renode.Utilities; 17 18 namespace Antmicro.Renode.Peripherals.SPI 19 { 20 public sealed class STM32H7_QuadSPI : NullRegistrationPointPeripheralContainer<GenericSpiFlash>, IKnownSize, 21 IDoubleWordPeripheral, IWordPeripheral, IBytePeripheral 22 { 23 // NOTE: Current implementation does not support DMA interface STM32H7_QuadSPI(IMachine machine)24 public STM32H7_QuadSPI(IMachine machine) : base(machine) 25 { 26 registers = new DoubleWordRegisterCollection(this); 27 DefineRegisters(); 28 } 29 Reset()30 public override void Reset() 31 { 32 lock(locker) 33 { 34 pollingTokenSource.Cancel(); 35 pollingTokenSource = new CancellationTokenSource(); 36 37 registers.Reset(); 38 ClearTransferFifo(); 39 40 skipInstruction = true; 41 skipAddress = true; 42 skipAlternateBytes = true; 43 skipData = true; 44 45 IRQ.Unset(); 46 } 47 } 48 ReadDoubleWord(long offset)49 public uint ReadDoubleWord(long offset) 50 { 51 return registers.Read(offset); 52 } 53 WriteDoubleWord(long offset, uint value)54 public void WriteDoubleWord(long offset, uint value) 55 { 56 registers.Write(offset, value); 57 } 58 ReadWord(long offset)59 public ushort ReadWord(long offset) 60 { 61 if(!CheckDataRegisterOffset(offset)) 62 { 63 return 0; 64 } 65 return (ushort)ReadFromDataRegister(16); 66 } 67 WriteWord(long offset, ushort value)68 public void WriteWord(long offset, ushort value) 69 { 70 if(!CheckDataRegisterOffset(offset)) 71 { 72 return; 73 } 74 WriteToDataRegister(value, 16); 75 } 76 ReadByte(long offset)77 public byte ReadByte(long offset) 78 { 79 if(!CheckDataRegisterOffset(offset)) 80 { 81 return 0; 82 } 83 return (byte)ReadFromDataRegister(8); 84 } 85 WriteByte(long offset, byte value)86 public void WriteByte(long offset, byte value) 87 { 88 if(!CheckDataRegisterOffset(offset)) 89 { 90 return; 91 } 92 WriteToDataRegister(value, 8); 93 } 94 95 public long Size => 0x1000; 96 public GPIO IRQ { get; } = new GPIO(); 97 98 // This value can be used to affect the interval between next polling events 99 // since on real HW it depends on the clock speed that is configured for QSPI kernel 100 public ulong PollingMultiplier { get; set; } = 1; 101 CheckDataRegisterOffset(long offset)102 private bool CheckDataRegisterOffset(long offset) 103 { 104 if(offset != (long)Registers.Data) 105 { 106 this.Log(LogLevel.Error, "This peripheral can only handle non 32-bit access at {0}. This operation has no effect", Registers.Data); 107 return false; 108 } 109 return true; 110 } 111 DefineRegisters()112 private void DefineRegisters() 113 { 114 Registers.Control.Define(registers) 115 .WithFlag(0, out enabled, name: "Enable") 116 .WithFlag(1, 117 writeCallback: (_, value) => 118 { 119 if(value) 120 { 121 lock(locker) 122 { 123 pollingTokenSource.Cancel(); 124 pollingTokenSource = new CancellationTokenSource(); 125 remainingBytesToTransfer = null; 126 } 127 } 128 }, 129 name: "Abort") 130 .WithReservedBits(2, 1) 131 .WithTaggedFlag("Timeout counter enable", 3) 132 .WithTaggedFlag("Sample shift", 4) 133 .WithReservedBits(5, 1) 134 .WithTaggedFlag("Dual-flash mode", 6) 135 .WithTaggedFlag("Flash memory selection", 7) 136 .WithValueField(8, 5, out fifoThreshold, name: "FIFO threshold level") 137 .WithReservedBits(13, 3) 138 .WithTaggedFlag("Transfer error interrupt enable", 16) 139 .WithFlag(17, out transferCompleteIrqEnable, name: "Transfer complete interrupt enable") 140 .WithFlag(18, out fifoThresholdInterruptEnable, name: "FIFO threshold interrupt enable") 141 .WithFlag(19, out statusMatchInterruptEnable, name: "Status match interrupt enable") 142 .WithTaggedFlag("Timeout interrupt enable", 20) 143 .WithReservedBits(21, 1) 144 .WithFlag(22, out pollingModeStopOnMatch, name: "Automatic status-polling mode stop") 145 .WithEnumField(23, 1, out pollingMatchMode, name: "Polling match mode") 146 .WithTag("Prescaler", 24, 8) 147 .WithWriteCallback((_, __) => UpdateInterrupts()); 148 149 Registers.DeviceConfiguration.Define(registers) 150 .WithTaggedFlag("Clock mode (mode 0/mode 3)", 0) 151 .WithReservedBits(1, 7) 152 .WithTag("Chip select high time", 8, 3) 153 .WithReservedBits(11, 5) 154 .WithValueField(16, 5, out flashSize, name: "Flash memory size") 155 .WithReservedBits(21, 11); 156 157 Registers.Status.Define(registers) 158 .WithFlag(0, FieldMode.Read, name: "Transmit error flag") 159 .WithFlag(1, out transferComplete, FieldMode.Read, name: "Transfer complete flag") 160 .WithFlag(2, out fifoThresholdReached, FieldMode.Read, name: "FIFO threshold flag") 161 .WithFlag(3, out statusMatch, FieldMode.Read, name: "Status match flag") 162 .WithFlag(4, FieldMode.Read, name: "Timeout flag") 163 .WithFlag(5, FieldMode.Read, name: "Busy") 164 .WithReservedBits(6, 2) 165 .WithValueField(8, 6, FieldMode.Read, 166 valueProviderCallback: _ => 167 { 168 switch(functionalMode.Value) 169 { 170 case ModeOfOperation.AutomaticStatusPolling: 171 case ModeOfOperation.MemoryMapped: 172 return 0; 173 case ModeOfOperation.IndirectRead: 174 case ModeOfOperation.IndirectWrite: 175 var count = transferFifo.Count; 176 return (count > MaximumFifoDepth) ? MaximumFifoDepth : (ulong)count; 177 default: 178 throw new InvalidOperationException($"Invalid mode: {functionalMode.Value}"); 179 } 180 }, 181 name: "FIFO level") 182 .WithReservedBits(14, 18); 183 184 Registers.FlagClear.Define(registers) 185 .WithFlag(0, FieldMode.WriteOneToClear, name: "Transmit error flag clear") 186 .WithFlag(1, FieldMode.WriteOneToClear, writeCallback: (_, value) => transferComplete.Value = false, name: "Transfer complete flag clear") 187 .WithReservedBits(2, 1) 188 .WithFlag(3, FieldMode.WriteOneToClear, writeCallback: (_, value) => statusMatch.Value = false, name: "Status match flag clear") 189 .WithFlag(4, FieldMode.WriteOneToClear, name: "Timeout flag clear") 190 .WithReservedBits(5, 27) 191 .WithWriteCallback((_, __) => UpdateInterrupts()); 192 193 Registers.DataLength.Define(registers) 194 .WithValueField(0, 32, out dataSize, name: "Data length"); 195 196 Registers.CommunicationConfiguration.Define(registers) 197 .WithValueField(0, 8, out instruction, name: "Instruction") 198 .WithValueField(8, 2, writeCallback: (_, value) => skipInstruction = value == 0, name: "Instruction mode") 199 .WithValueField(10, 2, writeCallback: (_, value) => skipAddress = value == 0, name: "Address mode") 200 .WithValueField(12, 2, out addressSize, name: "Address size") 201 .WithValueField(14, 2, writeCallback: (_, value) => skipAlternateBytes = value == 0, name: "Alternate byte mode") 202 .WithValueField(16, 2, out alternateBytesSize, name: "Alternate byte size") 203 .WithTag("Number of dummy cycles", 18, 5) 204 .WithReservedBits(23, 1) 205 .WithValueField(24, 2, writeCallback: (_, value) => skipData = value == 0, name: "Data mode") 206 .WithEnumField(26, 2, out functionalMode, 207 writeCallback: (oldValue, value) => 208 { 209 // Can't use `changeCallback` here, since it will trigger after the write callback, that is hooked to triggering the transfer 210 // this could cause the queue to loose all data immediately after the transfer happens, if there was a mode change directly before 211 if(oldValue != value) 212 { 213 ClearTransferFifo(); 214 pollingTokenSource.Cancel(); 215 pollingTokenSource = new CancellationTokenSource(); 216 UpdateInterrupts(); 217 } 218 }, 219 name: "Functional mode") 220 .WithTaggedFlag("Send instruction only once", 28) 221 .WithTaggedFlag("Free-running clock mode", 29) 222 .WithTaggedFlag("DDR hold", 30) 223 .WithTaggedFlag("DDR mode", 31) 224 // This callback needs to be triggered last, after all other fields are populated and callbacks triggered 225 .WithWriteCallback((_, __) => TriggerTransfer(TriggerTransferSource.Instruction)); 226 227 Registers.Address.Define(registers) 228 .WithValueField(0, 32, out address, name: "Address") 229 .WithWriteCallback(writeCallback: (_, __) => TriggerTransfer(TriggerTransferSource.Address)); 230 231 Registers.AlternateByte.Define(registers) 232 .WithValueField(0, 32, out alternateBytes, name: "Alternate bytes"); 233 234 Registers.Data.Define(registers) 235 .WithValueField(0, 32, 236 writeCallback: (_, value) => 237 { 238 WriteToDataRegister(value, 32); 239 }, 240 valueProviderCallback: _ => 241 { 242 return ReadFromDataRegister(32); 243 }, 244 name: "Data bytes"); 245 246 Registers.PollingStatusMask.Define(registers) 247 .WithValueField(0, 32, out pollingMask, name: "Polling status mask"); 248 249 Registers.PollingStatusMatch.Define(registers) 250 .WithValueField(0, 32, out pollingReferenceMatch, name: "Polling status match"); 251 252 Registers.PollingInterval.Define(registers) 253 .WithValueField(0, 16, out pollingInterval, name: "Polling interval") 254 .WithReservedBits(16, 16); 255 } 256 ClearTransferFifo()257 private void ClearTransferFifo() 258 { 259 transferFifo.Clear(); 260 remainingBytesToTransfer = null; 261 } 262 263 // For clarity, sizes are given in number of bits CheckDataRegisterAccessSize(int size)264 private void CheckDataRegisterAccessSize(int size) 265 { 266 if(size % 8 != 0 || size / 8 > 4) 267 { 268 throw new ArgumentException($"Size {size} is not properly constrained, cannot access data register"); 269 } 270 } 271 WriteToDataRegister(ulong val, int size)272 private void WriteToDataRegister(ulong val, int size) 273 { 274 CheckDataRegisterAccessSize(size); 275 if(functionalMode.Value != ModeOfOperation.IndirectWrite) 276 { 277 this.Log(LogLevel.Error, "Data register should only be written in {0} mode. This operation has no effect", ModeOfOperation.IndirectWrite); 278 return; 279 } 280 lock(locker) 281 { 282 transferFifo.EnqueueRange(BitHelper.GetBytesFromValue(val, size / 8)); 283 UpdateInterrupts(); 284 TriggerTransfer(TriggerTransferSource.DataWrite); 285 } 286 } 287 ReadFromDataRegister(int size)288 private uint ReadFromDataRegister(int size) 289 { 290 CheckDataRegisterAccessSize(size); 291 if(!(functionalMode.Value == ModeOfOperation.IndirectRead || functionalMode.Value == ModeOfOperation.AutomaticStatusPolling)) 292 { 293 this.Log(LogLevel.Error, "Data register should not be read in mode: {0}. Returning 0", functionalMode.Value); 294 return 0; 295 } 296 byte[] response = new byte[4]; 297 lock(locker) 298 { 299 for(int i = 0; i < size / 8; ++i) 300 { 301 if(transferFifo.TryDequeue(out var val)) 302 { 303 // Push the value back into the queue immediately in AutomaticStatusPolling mode, so it can be read from Data register again 304 // It's guaranteed in `ReceiveData` that the queue won't grow beyond 4-byte limit 305 if(functionalMode.Value == ModeOfOperation.AutomaticStatusPolling) 306 { 307 transferFifo.Enqueue(val); 308 } 309 } 310 else 311 { 312 this.Log(LogLevel.Warning, "Read from empty transfer FIFO, containing less data than 32 bits. Filling with zeroes"); 313 } 314 response[i] = val; 315 } 316 if(functionalMode.Value == ModeOfOperation.AutomaticStatusPolling) 317 { 318 // In case of polling, if less than the full size of the queue is requested, pop and push the data back, so it's not corrupted 319 for(int i = size / 8; i < 4; ++i) 320 { 321 if(transferFifo.TryDequeue(out var val)) 322 { 323 transferFifo.Enqueue(val); 324 } 325 } 326 } 327 UpdateInterrupts(); 328 TriggerTransfer(TriggerTransferSource.DataRead); 329 } 330 return BitHelper.ToUInt32(response, 0, 4, true); 331 } 332 UpdateInterrupts()333 private void UpdateInterrupts() 334 { 335 bool thresholdReached = functionalMode.Value == ModeOfOperation.IndirectRead && (transferFifo.Count >= (int)fifoThreshold.Value); 336 thresholdReached |= functionalMode.Value == ModeOfOperation.IndirectWrite && ((MaximumFifoDepth - transferFifo.Count) >= (int)fifoThreshold.Value); 337 fifoThresholdReached.Value = thresholdReached; 338 339 bool status = (transferComplete.Value && transferCompleteIrqEnable.Value) 340 || (statusMatch.Value && statusMatchInterruptEnable.Value) 341 || (fifoThresholdReached.Value && fifoThresholdInterruptEnable.Value); 342 343 this.Log(LogLevel.Noisy, "IRQ set to: {0}", status); 344 IRQ.Set(status); 345 } 346 SplitToBytesAndSend(IValueRegisterField value, int size, string name)347 private void SplitToBytesAndSend(IValueRegisterField value, int size, string name) 348 { 349 var bytes = BitHelper.GetBytesFromValue(value.Value, size); 350 this.Log(LogLevel.Debug, "Sending {0}: {1}", name, Misc.PrettyPrintCollectionHex(bytes)); 351 foreach(var part in bytes) 352 { 353 RegisteredPeripheral.Transmit(part); 354 } 355 } 356 TriggerTransfer(TriggerTransferSource source)357 private void TriggerTransfer(TriggerTransferSource source) 358 { 359 if(!enabled.Value) 360 { 361 this.Log(LogLevel.Warning, "Trying to trigger SPI transfer, but the peripheral is disabled"); 362 return; 363 } 364 if(functionalMode.Value == ModeOfOperation.MemoryMapped) 365 { 366 this.Log(LogLevel.Debug, "The peripheral is in MemoryMapped mode, all transfers are ignored"); 367 return; 368 } 369 this.Log(LogLevel.Debug, "Trying to trigger SPI transfer, source: {0}, mode: {1}", source, functionalMode.Value); 370 371 lock(locker) 372 { 373 // If more data is expected in the command sequence, delay the transfer 374 // until the data is written into the relevant register 375 if(!VerifyIfTransferShouldTrigger(source)) 376 { 377 return; 378 } 379 380 // If a transfer requires more data to complete, then skip immediately to the data phase 381 if(remainingBytesToTransfer == null) 382 { 383 if(!skipInstruction) 384 { 385 this.Log(LogLevel.Debug, "Sending command: 0x{0:X}", instruction.Value); 386 RegisteredPeripheral.Transmit((byte)instruction.Value); 387 } 388 389 if(!skipAddress) 390 { 391 SplitToBytesAndSend(address, (int)addressSize.Value + 1, "address"); 392 } 393 394 if(!skipAlternateBytes) 395 { 396 SplitToBytesAndSend(alternateBytes, (int)alternateBytesSize.Value + 1, "alternate bytes"); 397 } 398 } 399 400 if(!skipData) 401 { 402 TransferData(); 403 HandlePollingData(); 404 } 405 else 406 { 407 // Transfer is only complete here, if there was no data to send 408 // since this function is re-entrant otherwise 409 RegisteredPeripheral.FinishTransmission(); 410 transferComplete.Value = true; 411 } 412 UpdateInterrupts(); 413 } 414 } 415 VerifyIfTransferShouldTrigger(TriggerTransferSource source)416 private bool VerifyIfTransferShouldTrigger(TriggerTransferSource source) 417 { 418 // Check if a write to this register (identified by `source`), should result in data transfer 419 // or if the peripheral is still waiting for more data (e.g. it got an instruction but it's missing data) 420 switch (source) 421 { 422 case TriggerTransferSource.Instruction: 423 if(skipInstruction && source == TriggerTransferSource.Instruction) 424 { 425 // If the source is Instruction register, but instruction stage is disabled, the transfer shouldn't trigger 426 this.Log(LogLevel.Noisy, "Not transferring, {0} is disabled", nameof(TriggerTransferSource.Instruction)); 427 return false; 428 } 429 if(skipAddress && !skipData) 430 { 431 // Data without address - let the next stage determine if it's valid 432 goto case TriggerTransferSource.Address; 433 } 434 if(!skipAddress) 435 { 436 this.Log(LogLevel.Noisy, "Address is missing, not transferring"); 437 // Wait for address 438 return false; 439 } 440 goto case TriggerTransferSource.Address; 441 case TriggerTransferSource.Address: 442 if(skipAddress && source == TriggerTransferSource.Address) 443 { 444 this.Log(LogLevel.Noisy, "Not transferring, {0} is disabled", nameof(TriggerTransferSource.Address)); 445 return false; 446 } 447 if(!skipData && functionalMode.Value == ModeOfOperation.IndirectWrite) 448 { 449 this.Log(LogLevel.Noisy, "Data is missing, not transferring"); 450 // Wait for data 451 return false; 452 } 453 // It's safe to break here, instead of jumping to data, since it can be a terminal stage too 454 break; 455 case TriggerTransferSource.DataWrite: 456 if(skipData && source == TriggerTransferSource.DataWrite) 457 { 458 this.Log(LogLevel.Noisy, "Not transferring, {0} is disabled", nameof(TriggerTransferSource.DataWrite)); 459 return false; 460 } 461 break; 462 case TriggerTransferSource.DataRead: 463 // Polling mode schedules transfers by its own 464 if(functionalMode.Value == ModeOfOperation.AutomaticStatusPolling) 465 { 466 return false; 467 } 468 // Only allow Data read to trigger transfer, if there already is a transfer ongoing 469 // and there was not enough space in the transfer FIFO, so the transfer was suspended 470 if(remainingBytesToTransfer == null) 471 { 472 this.Log(LogLevel.Noisy, "Not transferring, {0} transfer is complete", nameof(TriggerTransferSource.DataRead)); 473 return false; 474 } 475 break; 476 default: 477 this.ErrorLog("Trigger source {0} is unrecognized, not transferring", source); 478 return false; 479 } 480 return true; 481 } 482 DoesPolledDataMatch(uint polledUInt)483 private bool DoesPolledDataMatch(uint polledUInt) 484 { 485 switch(pollingMatchMode.Value) 486 { 487 case PollingMatchMode.AllBits: 488 // Since XOR returns 1 only on differing bits, zero means no change at all 489 // AND with the mask, and if equals zero, both values match on all bits 490 return ((polledUInt ^ pollingReferenceMatch.Value) & pollingMask.Value) == 0; 491 case PollingMatchMode.AnyBit: 492 // If the diff (AND mask) is equal to the mask bytes, that means there was no single bit matched 493 return ((polledUInt ^ pollingReferenceMatch.Value) & pollingMask.Value) != pollingMask.Value; 494 default: 495 throw new InvalidOperationException($"Invalid match mode: {pollingMatchMode.Value}"); 496 } 497 } 498 HandlePollingData()499 private void HandlePollingData() 500 { 501 if(functionalMode.Value != ModeOfOperation.AutomaticStatusPolling) 502 { 503 return; 504 } 505 506 uint responseUInt = ReadFromDataRegister((int)(dataSize.Value + 1) * 8); 507 bool matched = DoesPolledDataMatch(responseUInt); 508 if(matched) 509 { 510 statusMatch.Value = true; 511 } 512 513 if(!matched || !pollingModeStopOnMatch.Value) 514 { 515 var nextEvent = pollingInterval.Value * PollingMultiplier; 516 this.Log(LogLevel.Debug, "Polling not matched (got: 0x{0:X}, expected: 0x{1:X}, mask: 0x{2:X}), scheduling next polling in {3} microseconds", 517 responseUInt, pollingReferenceMatch.Value, pollingMask.Value, nextEvent); 518 var token = pollingTokenSource.Token; 519 Machine.ScheduleAction(TimeInterval.FromMicroseconds(nextEvent), _ => 520 { 521 lock(locker) 522 { 523 if(token.IsCancellationRequested) 524 { 525 return; 526 } 527 // Schedule a deferred polling action, if there was no match 528 transferFifo.Clear(); 529 TransferData(); 530 HandlePollingData(); 531 UpdateInterrupts(); 532 } 533 }, $"{nameof(STM32H7_QuadSPI)} Polling task"); 534 } 535 } 536 TransferData()537 private void TransferData() 538 { 539 if(skipData) 540 { 541 return; 542 } 543 if(functionalMode.Value == ModeOfOperation.AutomaticStatusPolling && dataSize.Value > 3) 544 { 545 // In Status polling only 4 bytes can be transferred at once, for the purpose of the comparison 546 this.Log(LogLevel.Error, "DataSize cannot be more than 3 when in {0} mode. Automatically clamping the comparison length to 3", 547 nameof(ModeOfOperation.AutomaticStatusPolling)); 548 dataSize.Value = 3; 549 } 550 551 if(remainingBytesToTransfer == null) 552 { 553 if(dataSize.Value == uint.MaxValue) 554 { 555 // Transfer all bytes, until the end of the flash memory 556 remainingBytesToTransfer = 2 << (int)(flashSize.Value + 1); 557 } 558 else 559 { 560 remainingBytesToTransfer = (int)dataSize.Value + 1; 561 } 562 } 563 564 byte d = 0; 565 while((functionalMode.Value != ModeOfOperation.IndirectWrite || transferFifo.TryDequeue(out d)) 566 && remainingBytesToTransfer > 0 && transferFifo.Count <= MaximumFifoDepth) 567 { 568 --remainingBytesToTransfer; 569 if(functionalMode.Value == ModeOfOperation.IndirectWrite) 570 { 571 this.Log(LogLevel.Debug, "Sending byte: 0x{0:X}, bytes left: {1}", d, remainingBytesToTransfer); 572 } 573 var recv = RegisteredPeripheral.Transmit(d); 574 if(functionalMode.Value != ModeOfOperation.IndirectWrite) 575 { 576 this.Log(LogLevel.Debug, "Got byte: 0x{0:X}, bytes left: {1}", recv, remainingBytesToTransfer); 577 ReceiveData(recv); 578 } 579 } 580 if(remainingBytesToTransfer == 0) 581 { 582 RegisteredPeripheral.FinishTransmission(); 583 transferComplete.Value = true; 584 remainingBytesToTransfer = null; 585 } 586 } 587 ReceiveData(byte data)588 private void ReceiveData(byte data) 589 { 590 if(functionalMode.Value == ModeOfOperation.IndirectWrite) 591 { 592 return; 593 } 594 transferFifo.Enqueue(data); 595 if(functionalMode.Value == ModeOfOperation.AutomaticStatusPolling) 596 { 597 // Status polling implies that only the last 4 values obtained (uint32) make their way into the FIFO 598 // So don't allow the queue to grow beyond this limit 599 if(transferFifo.Count > 4) 600 { 601 transferFifo.Dequeue(); 602 } 603 } 604 } 605 606 private const int MaximumFifoDepth = 32; 607 608 private IFlagRegisterField transferCompleteIrqEnable; 609 private IFlagRegisterField statusMatchInterruptEnable; 610 private IFlagRegisterField fifoThresholdInterruptEnable; 611 612 private IFlagRegisterField transferComplete; 613 private IFlagRegisterField statusMatch; 614 private IFlagRegisterField fifoThresholdReached; 615 private IFlagRegisterField pollingModeStopOnMatch; 616 617 // Sizes specified here are always less by one than the real transfer size (that's how the HW handles the registers) 618 // e.g. size 0 means that 1 byte is to be transferred 619 private IFlagRegisterField enabled; 620 private IValueRegisterField fifoThreshold; 621 private IValueRegisterField instruction; 622 private IValueRegisterField dataSize; 623 private IValueRegisterField address; 624 private IValueRegisterField addressSize; 625 private IValueRegisterField alternateBytes; 626 private IValueRegisterField alternateBytesSize; 627 // Number of bytes in flash = 2 ** (flashSize + 1) 628 private IValueRegisterField flashSize; 629 630 private IValueRegisterField pollingMask; 631 private IValueRegisterField pollingReferenceMatch; 632 private IValueRegisterField pollingInterval; 633 634 private bool skipInstruction = true; 635 private bool skipAddress = true; 636 private bool skipAlternateBytes = true; 637 private bool skipData = true; 638 private int? remainingBytesToTransfer; 639 private readonly object locker = new object(); 640 641 private IEnumRegisterField<ModeOfOperation> functionalMode; 642 private IEnumRegisterField<PollingMatchMode> pollingMatchMode; 643 644 private readonly DoubleWordRegisterCollection registers; 645 646 private readonly Queue<byte> transferFifo = new Queue<byte>(); 647 648 private CancellationTokenSource pollingTokenSource = new CancellationTokenSource(); 649 650 private enum TriggerTransferSource 651 { 652 Instruction, 653 Address, 654 DataWrite, 655 DataRead 656 } 657 658 private enum PollingMatchMode 659 { 660 AllBits = 0b0, // AND 661 AnyBit = 0b1, // OR 662 } 663 664 private enum ModeOfOperation 665 { 666 IndirectWrite = 0b00, 667 IndirectRead = 0b01, 668 AutomaticStatusPolling = 0b10, 669 // Note, that we can't map a flash memory to address range here 670 // this has to be a property of the flash itself (e.g. GenericSpiFlash: underlyingMemory) 671 MemoryMapped = 0b11, 672 } 673 674 private enum Registers 675 { 676 Control = 0x00, 677 DeviceConfiguration = 0x04, 678 Status = 0x08, 679 FlagClear = 0x0C, 680 DataLength = 0x10, 681 CommunicationConfiguration = 0x14, 682 Address = 0x18, 683 AlternateByte = 0x1C, 684 Data = 0x20, 685 PollingStatusMask = 0x24, 686 PollingStatusMatch = 0x28, 687 PollingInterval = 0x2C, 688 LowPowerTimeout = 0x30, 689 } 690 } 691 } 692