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 using System;
8 using System.Collections.Generic;
9 using System.Linq;
10 
11 using Antmicro.Renode.Core;
12 using Antmicro.Renode.Core.Structure;
13 using Antmicro.Renode.Core.Structure.Registers;
14 using Antmicro.Renode.Debugging;
15 using Antmicro.Renode.Logging;
16 using Antmicro.Renode.Peripherals.Bus;
17 using Antmicro.Renode.Utilities;
18 
19 namespace Antmicro.Renode.Peripherals.SPI
20 {
21     public class IMXRT_FlexSPI : NullRegistrationPointPeripheralContainer<ISPIPeripheral>, IDoubleWordPeripheral, IProvidesRegisterCollection<DoubleWordRegisterCollection>, IKnownSize
22     {
IMXRT_FlexSPI(IMachine machine)23         public IMXRT_FlexSPI(IMachine machine) : base(machine)
24         {
25             IRQ = new GPIO();
26             RegistersCollection = new DoubleWordRegisterCollection(this);
27             lutMemory = new byte[NumberOfLUTs * 4];
28 
29             rxQueue = new RandomAccessQueue(FifoDepth * 4);
30             txQueue = new RandomAccessQueue(FifoDepth * 4);
31 
32             commandsEngine = new CommandsEngine(this);
33 
34             DefineRegisters();
35             UpdateInterrupts();
36         }
37 
Reset()38         public override void Reset()
39         {
40             Array.Clear(lutMemory, 0, lutMemory.Length);
41 
42             rxQueue.Reset();
43             txQueue.Reset();
44 
45             isInTransmit = false;
46             totalReadCounter = 0;
47 
48             RegistersCollection.Reset();
49             commandsEngine.Reset();
50 
51             UpdateInterrupts();
52         }
53 
54         [ConnectionRegion("ciphertext")]
ReadDoubleWordFromCiphertext(long offset)55         public uint ReadDoubleWordFromCiphertext(long offset)
56         {
57             return ReadFromCiphertext(offset, 4);
58         }
59 
60         [ConnectionRegion("ciphertext")]
WriteDoubleWordToCiphertext(long offset, uint value)61         public void WriteDoubleWordToCiphertext(long offset, uint value)
62         {
63             WriteToCiphertext(offset, value, 4);
64         }
65 
66         [ConnectionRegion("ciphertext")]
ReadWordFromCiphertext(long offset)67         public ushort ReadWordFromCiphertext(long offset)
68         {
69             return (ushort)ReadFromCiphertext(offset, 2);
70         }
71 
72         [ConnectionRegion("ciphertext")]
WriteWordToCiphertext(long offset, ushort value)73         public void WriteWordToCiphertext(long offset, ushort value)
74         {
75             WriteToCiphertext(offset, value, 2);
76         }
77 
78         [ConnectionRegion("ciphertext")]
ReadByteFromCiphertext(long offset)79         public byte ReadByteFromCiphertext(long offset)
80         {
81             return (byte)ReadFromCiphertext(offset, 1);
82         }
83 
84         [ConnectionRegion("ciphertext")]
WriteByteToCiphertext(long offset, byte value)85         public void WriteByteToCiphertext(long offset, byte value)
86         {
87             WriteToCiphertext(offset, value, 1);
88         }
89 
ReadDoubleWord(long offset)90         public uint ReadDoubleWord(long offset)
91         {
92             return RegistersCollection.Read(offset);
93         }
94 
WriteDoubleWord(long offset, uint value)95         public void WriteDoubleWord(long offset, uint value)
96         {
97             RegistersCollection.Write(offset, value);
98         }
99 
100         public long Size => 0x4000;
101 
102         public GPIO IRQ { get; }
103 
104         public DoubleWordRegisterCollection RegistersCollection { get; }
105 
ReadFromCiphertext(long offset, int width)106         private uint ReadFromCiphertext(long offset, int width)
107         {
108             DebugHelper.Assert(width > 0 && width <= 4);
109 
110             // here we set parameters for the read command
111             // that will be executed by the commands engine
112             // note: this overwrites values in registers; not sure if this is how the actual HW works
113             dataSize.Value = (uint)width;
114             rxQueue.Reset();
115             serialFlashAddress.Value = (uint)offset;
116 
117             commandsEngine.LoadCommands((uint)ahbReadSequenceIndex.Value, (uint)ahbReadSequenceLength.Value + 1);
118             commandsEngine.Execute();
119 
120             var result = rxQueue.Read(0, width);
121 
122             this.Log(LogLevel.Debug, "Read {0}-byte{1} from ciphertext at offset 0x{2:X}: 0x{3:X}", width, width == 1 ? string.Empty : "s", offset, result);
123 
124             return result;
125         }
126 
WriteToCiphertext(long offset, uint value, int width)127         private void WriteToCiphertext(long offset, uint value, int width)
128         {
129             DebugHelper.Assert(width > 0 && width <= 4);
130             this.Log(LogLevel.Debug, "Writing {0}-byte{1} to ciphertext at offset 0x{2:X}: 0x{3:X}", width, width == 1 ? string.Empty : "s",  offset, value);
131 
132             // here we set parameters for the write command
133             // that will be executed by the commands engine
134             // note: this overwrites values in registers; not sure if this is how the actual HW works
135             dataSize.Value = (uint)width;
136             var count = txQueue.Fill(BitConverter.GetBytes(value).Take(width), reset: true);
137             if(count != width)
138             {
139                 this.Log(LogLevel.Warning, "Could not fit all bytes into FIFO - {0} of them were dropped", width - count);
140             }
141 
142             serialFlashAddress.Value = (uint)offset;
143 
144             commandsEngine.LoadCommands((uint)ahbWriteSequenceIndex.Value, (uint)ahbWriteSequenceLength.Value + 1);
145             commandsEngine.Execute();
146             TryPushData();
147         }
148 
DefineRegisters()149         private void DefineRegisters()
150         {
151             Registers.StatusRegister0.Define(this)
152                 .WithFlag(0, FieldMode.Read, name: "SEQIDLE - Command sequence idle ", valueProviderCallback: _ => !commandsEngine.InProgress)
153                 .WithFlag(1, FieldMode.Read, name: "ARBIDLE - Arbitrator idle", valueProviderCallback: _ => true) // when false: there is command sequence granted by arbitrator and not finished yet on FlexSPI interface
154                 .WithTag("ARBCMDSRC", 2, 2)
155                 .WithReservedBits(4, 28);
156 
157             Registers.IPCommandRegister.Define(this)
158                 .WithFlag(0, name: "TRG", writeCallback: (_, val) =>
159                 {
160                     if(val)
161                     {
162                         TriggerCommand();
163                     }
164                 })
165                 .WithReservedBits(1, 31);
166 
167             Registers.IPControlRegister0.Define(this)
168                 .WithValueField(0, 32, out serialFlashAddress, name: "SFAR - Serial Flash Address for IP command");
169 
170             Registers.IPControlRegister1.Define(this)
171                 .WithValueField(0, 16, out dataSize, name: "IDATSZ - Flash Read/Program Data Size (in bytes) for IP command")
172                 .WithValueField(16, 4, out sequenceIndex, name: "ISEQID - Sequence Index in LUT for IP command")
173                 .WithReservedBits(20, 4)
174                 .WithValueField(24, 3, out sequenceNumber, name: "ISEQNUM - Sequence Number for IP command: ISEQNUM+1")
175                 .WithReservedBits(27, 4)
176                 .WithTaggedFlag("IPAREN - Parallel mode Enabled for IP command", 31);
177 
178             Registers.LUT0.DefineMany(this, NumberOfLUTs, (register, idx) =>
179             {
180                 var j = idx;
181                 register.
182                     WithValueFields(0, 8, 4,
183                         valueProviderCallback: (i, _) => lutMemory[j * 0x4 + i],
184                         writeCallback: (i, _, value) => lutMemory[j * 0x4 + i] = (byte)value);
185             });
186 
187             Registers.IPRXFIFOControlRegister.Define(this)
188                 .WithFlag(0, name: "CLRIPRXF - Clear all valid data entries in IP RX FIFO",
189                     valueProviderCallback: _ => false, // this is not explicitly stated in the documentation
190                     writeCallback: (_, val) => { if(val) rxQueue.Reset(); })
191                 .WithTaggedFlag("RXDMAEN - IP RX FIFO reading by DMA enabled", 1)
192                 .WithValueField(2, 4, out rxWatermark, name: "RXWMRK - Watermark level")
193                 .WithReservedBits(6, 26)
194                 .WithWriteCallback((_, __) => UpdateInterrupts());
195 
196             Registers.IPTXFIFOControlRegister.Define(this)
197                 .WithFlag(0, name: "CLRIPTXF - Clear all valid data entries in IP TX FIFO",
198                     valueProviderCallback: _ => false, // this is not explicitly stated in the documentation
199                     writeCallback: (_, val) => { if(val) txQueue.Reset(); })
200                 .WithTaggedFlag("TXDMAEN - IP TX FIFO reading by DMA enabled", 1)
201                 .WithValueField(2, 4, out txWatermark, name: "TXWMRK - Watermark level")
202                 .WithReservedBits(6, 26)
203                 .WithWriteCallback((_, __) => UpdateInterrupts());
204 
205             Registers.InterruptRegister.Define(this)
206                 .WithFlag(0, out ipCommandDone, FieldMode.Read | FieldMode.WriteOneToClear, name: "IPCMDDONE - IP triggered Command Sequences Execution finished interrupt")
207                 .WithTaggedFlag("AHBCMDERR - AHB triggered Command Sequences Error Detected interrupt", 1)
208                 .WithTaggedFlag("IPCMDGE - IP triggered Command Sequences Grant Timeout interrupt", 2)
209                 .WithTaggedFlag("AHBCMDGE - AHB triggered Command Sequences Grant Timeout interrupt", 3)
210                 .WithTaggedFlag("IPCMDERR - IP triggered Command Sequences Error Detected interrupt", 4)
211                 .WithFlag(5, out ipRxWatermarkAvailable, FieldMode.Read | FieldMode.WriteOneToClear, name: "IPRXWA - IP RX FIFO watermark available interrupt")
212                 .WithFlag(6, out ipTxWatermarkEmpty, FieldMode.Read | FieldMode.WriteOneToClear, name: "IPTXWE - IP TX FIFO watermark empty interrupt", writeCallback: (_, val) => { if(val) TryPushData(); })
213                 .WithReservedBits(7, 1)
214                 .WithTaggedFlag("SEQTIMEOUT - Sequence execution timeout interrupt", 8)
215                 .WithTaggedFlag("SCKSTOPBYRD - SCLK is stopped during command sequence because Async RX FIFO full interrupt", 9)
216                 .WithTaggedFlag("SCKSTOPBYWR - SCLK is stopped during command sequence because Async TX FIFO empty interrupt", 10)
217                 .WithTaggedFlag("AHBBUSTIMEOUT - AHB Bus timeout interrupt", 11)
218                 .WithReservedBits(12, 20)
219                 .WithWriteCallback((_, __) => UpdateInterrupts());
220 
221             Registers.InterruptEnableRegister.Define(this)
222                 .WithFlag(0, out ipCommandDoneEnabled, name: "IPCMDDONEEN - IP triggered Command Sequences Execution finished interrupt enable")
223                 .WithTaggedFlag("AHBCMDERREN - AHB triggered Command Sequences Error Detected interrupt enable", 1)
224                 .WithTaggedFlag("IPCMDGEEN - IP triggered Command Sequences Grant Timeout interrupt enable", 2)
225                 .WithTaggedFlag("AHBCMDGEEN - AHB triggered Command Sequences Grant Timeout interrupt enable", 3)
226                 .WithTaggedFlag("IPCMDERREN - IP triggered Command Sequences Error Detected interrupt enable", 4)
227                 .WithFlag(5, out ipRxWatermarkAvailableEnabled, name: "IPRXWAEN - IP RX FIFO watermark available interrupt enable")
228                 .WithFlag(6, out ipTxWatermarkEmptyEnabled, name: "IPTXWEEN - IP TX FIFO watermark empty interrupt enable")
229                 .WithReservedBits(7, 1)
230                 .WithTaggedFlag("SEQTIMEOUTEN - Sequence execution timeout interrupt enable", 8)
231                 .WithTaggedFlag("SCKSTOPBYRDEN - SCLK is stopped during command sequence because Async RX FIFO full interrupt enable", 9)
232                 .WithTaggedFlag("SCKSTOPBYWREN - SCLK is stopped during command sequence because Async TX FIFO empty interrupt enable", 10)
233                 .WithTaggedFlag("AHBBUSTIMEOUTEN - AHB Bus timeout interrupt enable", 11)
234                 .WithReservedBits(12, 20)
235                 .WithWriteCallback((_, __) => UpdateInterrupts());
236 
237             Registers.IPRXFIFOStatusRegister.Define(this)
238                 .WithValueField(0, 8, FieldMode.Read, name: "FILL - Fill level of IP RX FIFO", valueProviderCallback: _ => (uint)rxQueue.FillLevel)
239                 .WithReservedBits(8, 8)
240                 .WithTag("RDCNTR - Total Read Data Counter", 16, 16);
241 
242             Registers.IPRXFIFODataRegister0.DefineMany(this, FifoDepth, (register, idx) =>
243             {
244                 var j = idx;
245                 register
246                     .WithValueField(0, 32, FieldMode.Read, name: "RXDATA", valueProviderCallback: _ =>
247                     {
248                         totalReadCounter += 4;
249                         return rxQueue.Read(4 * j);
250                     });
251             });
252 
253             Registers.IPTXFIFODataRegister0.DefineMany(this, FifoDepth, (register, idx) =>
254             {
255                 var j = idx;
256                 register
257                     .WithValueField(0, 32, FieldMode.Write, name: "TXDATA", writeCallback: (_, val) =>
258                     {
259                         // since the position is based on the registed id
260                         // there is no possiblity of running out of the buffer
261                         txQueue.Fill(position: 4 * j, data: new []
262                         {
263                             (byte)(val >> 24),
264                             (byte)(val >> 16),
265                             (byte)(val >> 8),
266                             (byte)(val)
267                         });
268                         UpdateInterrupts();
269                     });
270             });
271 
272             Registers.FlashA1ControlRegister2.Define(this)
273                 .WithValueField(0, 4, out ahbReadSequenceIndex, name: "ARDSEQID - Sequence Index for AHB Read triggered Command in LUT")
274                 .WithReservedBits(4, 1)
275                 .WithValueField(5, 3, out ahbReadSequenceLength, name: "ARDSEQNUM - Sequence Number for AHB Read triggered Command in LUT")
276                 .WithValueField(8, 4, out ahbWriteSequenceIndex, name: "AWRSEQID - Sequence Index for AHB Write triggered Command")
277                 .WithReservedBits(12, 1)
278                 .WithValueField(13, 3, out ahbWriteSequenceLength, name: "AWRSEQNUM - Sequence Number for AHB Write triggered Command")
279                 .WithTag("AWRWRITE", 16, 12)
280                 .WithTag("AWRWAITUNIT", 28, 3)
281                 .WithTag("CLRINSTRPTR", 31, 1);
282         }
283 
TryPushData()284         private bool TryPushData()
285         {
286             lock(txQueue)
287             {
288                 if(!isInTransmit)
289                 {
290                     // it's fine to just return without issuing a warning;
291                     // the transmit process is multi-step and there might
292                     // be different ordering of operations
293                     return false;
294                 }
295 
296                 this.Log(LogLevel.Debug, "Trying to push data to the device");
297                 if(!TryGetDevice(out var device))
298                 {
299                     this.Log(LogLevel.Warning, "Tried to push data to the device, but nothing is connected");
300                     return false;
301                 }
302 
303                 var dequeuedBytes = txQueue.Dequeue((int)dataSize.Value);
304                 if(dequeuedBytes.Length == 0)
305                 {
306                     this.Log(LogLevel.Warning, "Tried to push data to the device, but there is nothing in the queue");
307                     return false;
308                 }
309 
310                 this.Log(LogLevel.Debug, "Pushing {0} bytes of data to the device", dequeuedBytes.Length);
311 
312                 foreach(var b in dequeuedBytes)
313                 {
314                     device.Transmit(b);
315                 }
316 
317                 dataSize.Value -= (uint)dequeuedBytes.Length;
318                 UpdateInterrupts();
319 
320                 if(dataSize.Value == 0)
321                 {
322                     this.Log(LogLevel.Debug, "Programming finished");
323                     isInTransmit = false;
324                     if(commandsEngine.InProgress)
325                     {
326                         // execute the rest of commands
327                         commandsEngine.Execute();
328                     }
329                 }
330                 else
331                 {
332                     this.Log(LogLevel.Debug, "There is {0} more bytes to send left", dataSize.Value);
333                 }
334 
335                 return true;
336             }
337         }
338 
TryGetDevice(out ISPIPeripheral device)339         private bool TryGetDevice(out ISPIPeripheral device)
340         {
341             device = RegisteredPeripheral;
342             if(device == null)
343             {
344                 this.Log(LogLevel.Warning, "No device connected");
345                 return false;
346             }
347 
348             return true;
349         }
350 
TriggerCommand()351         private void TriggerCommand()
352         {
353             if(commandsEngine.InProgress)
354             {
355                 this.Log(LogLevel.Warning, "Tried to trigger a command, but the previous one is still in progress");
356                 return;
357             }
358 
359             // sequenceNumber's value in register is 1 less than the actual value
360             commandsEngine.LoadCommands((uint)sequenceIndex.Value, (uint)sequenceNumber.Value + 1);
361             commandsEngine.Execute();
362         }
363 
UpdateInterrupts()364         private void UpdateInterrupts()
365         {
366             var flag = false;
367 
368             ipTxWatermarkEmpty.Value = txQueue.EmptyLevel >= (int)txWatermark.Value;
369             ipRxWatermarkAvailable.Value = rxQueue.FillLevel >= (int)rxWatermark.Value;
370 
371             flag |= ipCommandDone.Value && ipCommandDoneEnabled.Value;
372             flag |= ipTxWatermarkEmpty.Value && ipTxWatermarkEmptyEnabled.Value;
373             flag |= ipRxWatermarkAvailable.Value && ipRxWatermarkAvailableEnabled.Value;
374 
375             this.Log(LogLevel.Debug, "Setting IRQ flag to {0}", flag);
376             IRQ.Set(flag);
377         }
378 
379         private IValueRegisterField sequenceIndex;
380         private IValueRegisterField sequenceNumber;
381         private IValueRegisterField dataSize;
382 
383         private IValueRegisterField ahbWriteSequenceIndex;
384         private IValueRegisterField ahbWriteSequenceLength;
385 
386         private IValueRegisterField ahbReadSequenceIndex;
387         private IValueRegisterField ahbReadSequenceLength;
388 
389         private IValueRegisterField serialFlashAddress;
390 
391         private IFlagRegisterField ipCommandDone;
392         private IFlagRegisterField ipCommandDoneEnabled;
393 
394         private IFlagRegisterField ipRxWatermarkAvailable;
395         private IFlagRegisterField ipRxWatermarkAvailableEnabled;
396 
397         private IFlagRegisterField ipTxWatermarkEmpty;
398         private IFlagRegisterField ipTxWatermarkEmptyEnabled;
399 
400         private IValueRegisterField rxWatermark;
401         private IValueRegisterField txWatermark;
402 
403         private bool isInTransmit;
404         private uint totalReadCounter;
405 
406         private readonly CommandsEngine commandsEngine;
407         private readonly RandomAccessQueue txQueue;
408         private readonly RandomAccessQueue rxQueue;
409         private readonly byte[] lutMemory;
410 
411         private const int NumberOfLUTs = 64;
412         private const int SequenceLength = 8;
413         private const int FifoDepth = 32;
414 
415         private class CommandsEngine
416         {
CommandsEngine(IMXRT_FlexSPI parent)417             public CommandsEngine(IMXRT_FlexSPI parent)
418             {
419                 this.parent = parent;
420                 descriptors = new List<IPCommandDescriptor>();
421             }
422 
Reset()423             public void Reset()
424             {
425                 currentCommand = 0;
426                 descriptors.Clear();
427             }
428 
LoadCommands(uint index, uint number)429             public void LoadCommands(uint index, uint number)
430             {
431                 if(currentCommand != descriptors.Count)
432                 {
433                     parent.Log(LogLevel.Warning, "Loading new commands, but there are some left");
434                     descriptors.Clear();
435                     currentCommand = 0;
436                 }
437 
438                 for(var i = 0u; i < number; i++)
439                 {
440                     foreach(var cmd in DecodeCommands(index + i))
441                     {
442                         descriptors.Add(cmd);
443                     }
444                 }
445             }
446 
Execute()447             public void Execute()
448             {
449                 if(currentCommand == descriptors.Count)
450                 {
451                     // there is nothing more to do
452                     InProgress = false;
453                     return;
454                 }
455 
456                 if(!parent.TryGetDevice(out var device))
457                 {
458                     return;
459                 }
460 
461                 InProgress = true;
462                 while(currentCommand < descriptors.Count)
463                 {
464                     if(!HandleCommand(descriptors[currentCommand++], device))
465                     {
466                         // come commands (e.g. write) are multi-step
467                         // and do not finish right away;
468                         // we need to exit the execution loop and wait
469                         // for them to complete before handling the next
470                         // command
471                         return;
472                     }
473                 }
474                 InProgress = false;
475             }
476 
477             public bool InProgress { get; private set; }
478 
DecodeCommands(uint sequenceIndex)479             private IEnumerable<IPCommandDescriptor> DecodeCommands(uint sequenceIndex)
480             {
481                 var result = new List<IPCommandDescriptor>();
482                 var startingIndex = sequenceIndex * 0x10;
483 
484                 parent.Log(LogLevel.Debug, "Decoding command at starting index: {0}", startingIndex);
485 
486                 for(var i = 0; i < SequenceLength; i++)
487                 {
488                     var currentIndex = startingIndex + (2 * i);
489                     var instr = (short)((parent.lutMemory[currentIndex + 1] << 8) + parent.lutMemory[currentIndex]);
490                     var cmd = DecodeCommand(instr);
491                     result.Add(cmd);
492 
493                     if(cmd.Command == IPCommand.Stop)
494                     {
495                         break;
496                     }
497                 }
498                 return result;
499             }
500 
DecodeCommand(short instr)501             private IPCommandDescriptor DecodeCommand(short instr)
502             {
503                 return new IPCommandDescriptor
504                 {
505                     Command = (IPCommand)(instr >> 10),
506                     NumberOfPaddings = (instr >> 8) & 0x3,
507                     Operand = (byte)instr
508                 };
509             }
510 
ReadFromDevice(ISPIPeripheral device, int count)511             private byte[] ReadFromDevice(ISPIPeripheral device, int count)
512             {
513                 var result = new byte[count];
514                 for(var i = 0; i < result.Length; i++)
515                 {
516                     // send a dummy byte
517                     result[i] = device.Transmit(0);
518                 }
519 
520                 return result;
521             }
522 
HandleCommand(IPCommandDescriptor cmd, ISPIPeripheral device)523             private bool HandleCommand(IPCommandDescriptor cmd, ISPIPeripheral device)
524             {
525                 var result = true;
526                 parent.Log(LogLevel.Debug, "About to execute command {0}", cmd);
527 
528                 switch(cmd.Command)
529                 {
530                     case IPCommand.TransmitCommand_SDR:
531                         device.Transmit(cmd.Operand);
532                         break;
533 
534                     case IPCommand.ReceiveReadData_SDR:
535                         {
536                             var dataCount = (parent.dataSize.Value == 0)
537                                 ? cmd.Operand
538                                 : parent.dataSize.Value;
539 
540                             var data = ReadFromDevice(device, (int)dataCount);
541                             var count = parent.rxQueue.Enqueue(data);
542                             if(count != data.Length)
543                             {
544                                 parent.Log(LogLevel.Warning, "There is no more space left in the RX queue. {0} bytes were dropped", data.Length - count);
545                             }
546                         }
547                         break;
548 
549                     case IPCommand.Stop:
550                         {
551                             device.FinishTransmission();
552                             parent.ipCommandDone.Value = true;
553                             parent.UpdateInterrupts();
554                         }
555                         break;
556 
557 
558                     case IPCommand.TransmitProgrammingData_SDR:
559                         {
560                             parent.isInTransmit = true;
561                             // note: parent operation breaks execution of the command
562                             // waiting for the data to be written to FIFO
563                             result = false;
564                         }
565                         break;
566 
567                     case IPCommand.TransmitRowAddress_SDR:
568                         {
569                             var a0 = (byte)(parent.serialFlashAddress.Value);
570                             var a1 = (byte)(parent.serialFlashAddress.Value >> 8);
571                             var a2 = (byte)(parent.serialFlashAddress.Value >> 16);
572                             device.Transmit(a2);
573                             device.Transmit(a1);
574                             device.Transmit(a0);
575                         }
576                         break;
577 
578                     default:
579                         parent.Log(LogLevel.Info, "Unsupported IP command: {0}", cmd);
580                         break;
581                 }
582 
583                 return result;
584             }
585 
586             private int currentCommand;
587 
588             private readonly IMXRT_FlexSPI parent;
589             private readonly List<IPCommandDescriptor> descriptors;
590         }
591 
592         private class RandomAccessQueue
593         {
RandomAccessQueue(int size)594             public RandomAccessQueue(int size)
595             {
596                 internalBuffer = new byte[size];
597             }
598 
Reset()599             public void Reset()
600             {
601                 FillLevel = 0;
602                 Array.Clear(internalBuffer, 0, internalBuffer.Length);
603             }
604 
Read(int position, int width = 4)605             public uint Read(int position, int width = 4)
606             {
607                 DebugHelper.Assert(width > 0 && width <= 4);
608 
609                 if(position >= FillLevel)
610                 {
611                     return 0;
612                 }
613                 return BitHelper.ToUInt32(internalBuffer, position, Math.Min(width, FillLevel - position), true);
614             }
615 
Dequeue(int maxCount)616             public byte[] Dequeue(int maxCount)
617             {
618                 var bytesToDequeue = Math.Min(FillLevel, maxCount);
619                 var result = new byte[bytesToDequeue];
620 
621                 Array.Copy(internalBuffer, 0, result, 0, result.Length);
622 
623                 var bytesLeft = FillLevel - bytesToDequeue;
624                 for(int i = 0; i < bytesLeft; i++)
625                 {
626                     internalBuffer[i] = internalBuffer[i + bytesToDequeue];
627                 }
628 
629                 FillLevel = bytesLeft;
630                 return result;
631             }
632 
Enqueue(byte[] bs)633             public int Enqueue(byte[] bs)
634             {
635                 var counter = 0;
636                 foreach(var b in bs)
637                 {
638                     if(FillLevel == internalBuffer.Length)
639                     {
640                         break;
641                     }
642 
643                     internalBuffer[FillLevel++] = b;
644                     counter++;
645                 }
646 
647                 return counter;
648             }
649 
Fill(IEnumerable<byte> data, bool reset = false, int position = -1)650             public int Fill(IEnumerable<byte> data, bool reset = false, int position = -1)
651             {
652                 var counter = 0;
653                 if(reset)
654                 {
655                     FillLevel = 0;
656                 }
657 
658                 var idx = (position >= 0)
659                     ? position
660                     : FillLevel;
661 
662                 foreach(var d in data)
663                 {
664                     if(idx >= internalBuffer.Length)
665                     {
666                         break;
667                     }
668                     internalBuffer[idx++] = d;
669                     counter++;
670                 }
671 
672                 FillLevel = Math.Max(FillLevel, idx);
673 
674                 return counter;
675             }
676 
677             public int FillLevel { get; private set; }
678             public int EmptyLevel => internalBuffer.Length - FillLevel;
679 
680             private readonly byte[] internalBuffer;
681         }
682 
683         private struct IPCommandDescriptor
684         {
685             public IPCommand Command;
686             public int NumberOfPaddings;
687             public byte Operand;
688 
ToStringAntmicro.Renode.Peripherals.SPI.IMXRT_FlexSPI.IPCommandDescriptor689             public override string ToString()
690             {
691                 return $"[IPCommandDescriptor: {Command} (0x{Command:X}), NumberOfPaddings: {NumberOfPaddings}, Operand: 0x{Operand:X}]";
692             }
693         }
694 
695         private enum IPCommand
696         {
697             TransmitCommand_SDR = 0x1, // CMD_SDR
698             TransmitCommand_DDR = 0x21, // CMD_DDR
699 
700             TransmitRowAddress_SDR = 0x2, // RADDR_SDR
701             TransmitRowAddress_DDR = 0x22, // RADDR_DDR
702 
703             TransmitColumnAddress_SDR = 0x3, // CADDR_SDR
704             TransmitColumnAddress_DDR = 0x23, // CADDR_SDR
705 
706             TransmitModeBits1_SDR = 0x4, // MODE1_SDR
707             TransmitModeBits1_DDR = 0x24, // MODE1_DDR
708 
709             TransmitModeBits2_SDR = 0x5, // MODE2_SDR
710             TransmitModeBits2_DDR = 0x25, // MODE2_DDR
711 
712             TransmitModeBits4_SDR = 0x6, // MODE4_SDR
713             TransmitModeBits4_DDR = 0x26, // MODE4_DDR
714 
715             TransmitModeBits8_SDR = 0x7, // MODE8_SDR
716             TransmitModeBits8_DDR = 0x27, // MODE8_DDR
717 
718             TransmitProgrammingData_SDR = 0x8, // WRITE_SDR
719             TransmitProgrammingData_DDR = 0x28, // WRITE_DDR
720 
721             ReceiveReadData_SDR = 0x9, // READ_SDR
722             ReceiveReadData_DDR = 0x29, // READ_DDR
723 
724             ReceiveReadDataOrPreamble_SDR = 0xa, // LEARN_SDR
725             ReceiveReadDataOrPreamble_DDR = 0x2a, // LEARN_DDR
726 
727             DataSize_SDR = 0xb, // DATASZ_SDR
728             DataSize_DDR = 0x2b, // DATASZ_DDR
729 
730             Dummy_SDR = 0xc, // DUMMY_SDR
731             Dummy_DDR = 0x2c, // DUMMY_DDR
732 
733             DummyRWDS_SDR = 0xd, // DUMMY_RWDS_SDR
734             DummyRWDS_DDR = 0x2d, // DUMMY_RWDS_DDR
735 
736             JumpOnCS = 0x1F, // JMP_ON_CS
737             Stop = 0x00, // STOP
738         }
739 
740         private enum Registers : long
741         {
742             ModuleControlRegister0 = 0x0, // (MCR0) 32 RW FFFF_80C2h
743             ModuleControlRegister1 = 0x4, // (MCR1) 32 RW FFFF_FFFFh
744             ModuleControlRegister2 = 0x8, // (MCR2) 32 RW 2000_81F7h
745             AHBBusControlRegister = 0xC, // (AHBCR) 32 RW 0000_0018h
746             InterruptEnableRegister = 0x10, // (INTEN) 32 RW 0000_0000h
747             InterruptRegister = 0x14, // (INTR) 32 RW 0000_0000h
748             LUTKeyRegister = 0x18, //(LUTKEY) 32 RW 5AF0_5AF0h
749             LUTControlRegister = 0x1C, //(LUTCR) 32 RW 0000_0002h
750             AHBRXBuffer0ControlRegister0 = 0x20, //(AHBRXBUF0CR0) 32 RW 8000_0020h
751             AHBRXBuffer1ControlRegister0 = 0x24, //(AHBRXBUF1CR0) 32 RW 8001_0020h
752             AHBRXBuffer2ControlRegister0 = 0x28, //(AHBRXBUF2CR0) 32 RW 8002_0020h
753             AHBRXBuffer3ControlRegister0 = 0x2C, //(AHBRXBUF3CR0) 32 RW 8003_0020h
754             FlashA1ControlRegister0 = 0x60, //(FLSHA1CR0) 32 RW 0001_0000h
755             FlashA2ControlRegister0 = 0x64, //(FLSHA2CR0) 32 RW 0001_0000h
756             FlashB1ControlRegister0 = 0x68, //(FLSHB1CR0) 32 RW 0001_0000h
757             FlashB2ControlRegister0 = 0x6C, //(FLSHB2CR0) 32 RW 0001_0000h
758             FlashA1ControlRegister1 = 0x70, //(FLSHA1CR1) 32 RW 0000_0063h
759             FlashA2ControlRegister1 = 0x74, //(FLSHA2CR1) 32 RW 0000_0063h
760             FlashB1ControlRegister1 = 0x78, //(FLSHB1CR1) 32 RW 0000_0063h
761             FlashB2ControlRegister1 = 0x7C, //(FLSHB2CR1) 32 RW 0000_0063h
762             FlashA1ControlRegister2 = 0x80, //(FLSHA1CR2) 32 RW 0000_0000h
763             FlashA2ControlRegister2 = 0x84, //(FLSHA2CR2) 32 RW 0000_0000h
764             FlashB1ControlRegister2 = 0x88, //(FLSHB1CR2) 32 RW 0000_0000h
765             FlashB2ControlRegister2 = 0x8C, //(FLSHB2CR2) 32 RW 0000_0000h
766             FlashControlRegister4 = 0x94, //(FLSHCR4) 32 RW 0000_0000h
767             IPControlRegister0 = 0xA0, //(IPCR0) 32 RW 0000_0000h
768             IPControlRegister1 = 0xA4, //(IPCR1) 32 RW 0000_0000h
769             IPCommandRegister = 0xB0, //(IPCMD) 32 RW 0000_0000h
770             IPRXFIFOControlRegister = 0xB8, //(IPRXFCR) 32 RW 0000_0000h
771             IPTXFIFOControlRegister = 0xBC, //(IPTXFCR) 32 RW 0000_0000h
772             DLLAControlRegister0 = 0xC0, //(DLLACR) 32 RW 0000_0100h
773             DLLBControlRegister0 = 0xC4, //(DLLBCR) 32 RW 0000_0100h
774             StatusRegister0 = 0xE0, //(STS0) 32 RO 0000_0002h
775             StatusRegister1 = 0xE4, //(STS1) 32 RO 0000_0000h
776             StatusRegister2 = 0xE8, //(STS2) 32 RO 0100_0100h
777             AHBSuspendStatusRegister = 0xEC, //(AHBSPNDSTS) 32 RO 0000_0000h
778             IPRXFIFOStatusRegister = 0xF0, //(IPRXFSTS) 32 RO 0000_0000h
779             IPTXFIFOStatusRegister = 0xF4, //(IPTXFSTS) 32 RO 0000_0000h
780             IPRXFIFODataRegister0 = 0x100, // (RFDR0 - RFDR31) 32 RO 0000_0000h
781             IPTXFIFODataRegister0 = 0x180,
782             LUT0 = 0x200,
783         }
784     }
785 }
786