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 
8 using Antmicro.Renode.Peripherals.Bus;
9 using Antmicro.Renode.Core;
10 using Antmicro.Renode.Core.Structure;
11 using Antmicro.Renode.Core.Structure.Registers;
12 using System.Collections.Generic;
13 using Antmicro.Renode.Utilities;
14 using Antmicro.Renode.Logging;
15 using Antmicro.Renode.Exceptions;
16 
17 namespace Antmicro.Renode.Peripherals.SPI
18 {
19     [AllowedTranslations(AllowedTranslation.ByteToDoubleWord)]
20     public class MPFS_QSPI: NullRegistrationPointPeripheralContainer<Micron_MT25Q>, IDoubleWordPeripheral, IKnownSize
21     {
MPFS_QSPI(IMachine machine, uint size)22         public MPFS_QSPI(IMachine machine, uint size) : base(machine)
23         {
24             locker = new object();
25             IRQ = new GPIO();
26 
27             if(size < MinimumSize)
28             {
29                 throw new ConstructionException($"The {nameof(size)} argument can't be lower than {MinimumSize}.");
30             }
31             Size = size;
32 
33             var registerMap = new Dictionary<long, DoubleWordRegister>
34             {
35                 {(long)Registers.Control, new DoubleWordRegister(this, 0x402) //the docs report 0x502, but this lights up a reserved bit.
36                     .WithFlag(0, out enabled, name: "ENABLE")
37                     .WithFlag(1, FieldMode.Read, valueProviderCallback: _ => true, name: "MASTER")
38                     .WithFlag(2, out xipMode, name: "XIP")
39                     .WithEnumField<DoubleWordRegister, XIPAddressBytes>(3, 1, name: "XIPADDR")
40                     .WithReservedBits(4, 6)
41                     .WithTag("CLKIDLE", 10, 1)
42                     .WithTag("SAMPLE", 11, 2)
43                     .WithTag("QSPIMODE[0]", 13, 1)
44                     .WithTag("QSPIMODE[1]", 14, 2)
45                     .WithFlag(16, out x4Enabled, name: "FLAGS4X")
46                     .WithTag("CLKRATE", 24, 4)
47                 },
48 
49                 {(long)Registers.Frames, new DoubleWordRegister(this)
50                     .WithValueField(0, 16, writeCallback: (_, value) =>
51                         {
52                             BitHelper.UpdateWith(ref totalBytes, (uint)value, 0, 16);
53                             bytesSent = 0;
54                         }, valueProviderCallback: _ => totalBytes, name: "TOTALBYTES")
55                     .WithValueField(16, 9, out commandBytes, name: "CMDBYTES")
56                     .WithTag("QSPI", 25, 1)
57                     .WithValueField(26, 4, writeCallback: (_, value) =>
58                         {
59                             idleCycles = (uint)value;
60                             bytesToSkip = value >= 8 ? 1u : 0;
61                             if(value != 0 && value != 8)
62                             {
63                                 this.Log(LogLevel.Warning, "Requested {0} idle cycles, but values other than 0 or 8 might not be properly handled.", value);
64                             }
65                             totalBytes += bytesToSkip;
66                         }, valueProviderCallback: _ => idleCycles, name: "IDLE")
67                     .WithFlag(30, valueProviderCallback: _ => false,
68                         // If set then the FIFO flags are set to byte mode
69                         changeCallback: (_, value) => x4Enabled.Value = false, name: "FLAGBYTE")
70                     .WithFlag(31, valueProviderCallback: _ => false,
71                         // If set then the FIFO flags are set to word mode
72                         changeCallback: (_, value) => x4Enabled.Value = true, name: "FLAGWORD")
73                     .WithWriteCallback((_, __) =>
74                     {
75                         txDone.Value = false;
76                         rxDone.Value = false;
77                         RefreshInterrupt();
78                     })
79                 },
80 
81                 {(long)Registers.InterruptEnable, new DoubleWordRegister(this)
82                     .WithFlag(0, out txDoneInterruptEnabled, name: "TXDONE")
83                     .WithFlag(1, out rxDoneInterruptEnabled, name: "RXDONE")
84                     .WithFlag(2, out rxAvailableInterruptEnabled, name: "RXAVAILABLE")
85                     .WithFlag(3, out txAvailableInterruptEnabled, name: "TXAVAILABLE")
86                     .WithFlag(4, out rxFifoEmptyInterruptEnabled, name: "RXFIFOEMPTY")
87                     .WithFlag(5, name: "TXFIFOFULL") //we keep the value, but not do anything with it, as this never happens
88                     .WithChangeCallback((_, __) => RefreshInterrupt())
89                 },
90 
91                 {(long)Registers.Status, new DoubleWordRegister(this)
92                     .WithFlag(0, out txDone, FieldMode.WriteOneToClear | FieldMode.Read, name: "TXDONE")
93                     .WithFlag(1, out rxDone, FieldMode.WriteOneToClear | FieldMode.Read, name: "RXDONE")
94                     .WithFlag(2, valueProviderCallback: _ => IsRxAvailable(), name: "RXAVAILABLE")
95                     .WithFlag(3, valueProviderCallback: _ => true, name: "TXAVAILABLE")
96                     .WithFlag(4, valueProviderCallback: _ => !IsRxAvailable(), name: "RXFIFOEMPTY")
97                     .WithFlag(5, valueProviderCallback: _ => false, name: "TXFIFOFULL")
98                     .WithReservedBits(6, 1)
99                     .WithFlag(7, valueProviderCallback: _ => true, name: "READY")
100                     .WithFlag(8, valueProviderCallback: _ => x4Enabled.Value, name: "FLAGSX4")
101                     .WithWriteCallback((_, __) => RefreshInterrupt())
102                 },
103 
104                 {(long)Registers.UpperAddress, new DoubleWordRegister(this)
105                     .WithValueField(0, 8, out upperAddress, name: "ADDRUP")
106                 },
107 
108                 {(long)Registers.RxData1, new DoubleWordRegister(this)
109                     .WithValueField(0, 8, FieldMode.Read,
110                         valueProviderCallback: _ =>
111                         {
112                             lock(locker)
113                             {
114                                 return (receiveFifo.Count > 0 ? receiveFifo.Dequeue() : 0u);
115                             }
116                         },
117                         name: "RXDATA")
118                     .WithWriteCallback((_, __) => RefreshInterrupt())
119                 },
120 
121                 {(long)Registers.TxData1, new DoubleWordRegister(this)
122                     .WithValueField(0, 8, FieldMode.Write, writeCallback: (_, val) => HandleByte((uint)val), name: "TXDATA")
123                 },
124 
125                 {(long)Registers.RxData4, new DoubleWordRegister(this)
126                 // The documentation is ambiguous on this register.
127                 // It says 4 bytes must be read from the FIFO, but does not state precisely what happens
128                 // when there is not enough data. This model ignores the read until there are at least 4 bytes to be read.
129                     .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ =>
130                     {
131                         lock(locker)
132                         {
133                             if(receiveFifo.Count >= 4)
134                             {
135                                 var value = 0u;
136                                 for(var i = 0; i < 4; ++i)
137                                 {
138                                     value |= (uint)receiveFifo.Dequeue() << (8 * i);
139                                 }
140                                 return value;
141                             }
142                         }
143                         this.Log(LogLevel.Warning, "Trying to read 4 bytes from the receive FIFO, but there are only {0} bytes available.",  receiveFifo.Count);
144                         return 0;
145                     },
146                     name: "RXDATA4")
147                     .WithWriteCallback((_, __) => RefreshInterrupt())
148                 },
149 
150                 {(long)Registers.TxData4, new DoubleWordRegister(this)
151                     .WithValueField(0, 32, FieldMode.Write,
152                         writeCallback: (_, val) =>
153                         {
154                             for(var i = 0; i < 4; i++)
155                             {
156                                 HandleByte(BitHelper.GetValue((uint)val, i * 8, 8));
157                             }
158                         },
159                         name: "TXDATA4")
160                 },
161                 // this register intentionally exposes the whole register for reading and the upper bytes for writing
162                 {(long)Registers.FramesUpper, new DoubleWordRegister(this)
163                     .WithValueField(0, 16, FieldMode.Read, valueProviderCallback: _ => totalBytes, name: "BYTESLOWER")
164                     .WithValueField(16, 16, writeCallback: (_, value) => BitHelper.UpdateWithShifted(ref totalBytes, (uint)value, 16, 16),
165                         valueProviderCallback: _ => totalBytes >> 16, name: "BYTESUPPER")
166                 }
167             };
168             registers = new DoubleWordRegisterCollection(this, registerMap);
169         }
170 
ReadDoubleWord(long offset)171         public uint ReadDoubleWord(long offset)
172         {
173             return xipMode.Value
174                 ? RegisteredPeripheral.UnderlyingMemory.ReadDoubleWord(BitHelper.SetMaskedValue((uint)offset, (uint)upperAddress.Value, 24, 8))
175                 : registers.Read(offset);
176         }
177 
WriteDoubleWord(long offset, uint value)178         public void WriteDoubleWord(long offset, uint value)
179         {
180             offset %= RegisterAliasSize;
181             registers.Write(offset, value);
182         }
183 
Reset()184         public override void Reset()
185         {
186             registers.Reset();
187             bytesSent = 0;
188             idleCycles = 0;
189             bytesToSkip = 0;
190             RefreshInterrupt();
191             lock(locker)
192             {
193                 receiveFifo.Clear();
194             }
195         }
196 
197         public GPIO IRQ { get; }
198         public long Size { get; }
199 
TryReceive(byte data)200         private void TryReceive(byte data)
201         {
202             lock(locker)
203             {
204                 receiveFifo.Enqueue(data);
205             }
206         }
207 
208         /*
209          * Method for handling commands sent to flash
210          *
211          * Every operation will consist of at least 1 byte - the command code.
212          *
213          * However, we can distinguish 4 types of flash operations based on the relation between
214          * number of command address bytes and data bytes (a generalization):
215          * 1) 0 address bytes and 0 data bytes
216          * 2) 0 address bytes and at least 1 data byte
217          * 3) 3 or 4 address bytes and 0 data bytes
218          * 4) 3 or 4 address bytes and at least 1 data byte
219          *
220          * Description of significant variables:
221          * a) `commandBytes` = command byte + address bytes
222          * b) `totalBytes` = `commandBytes` + data bytes
223          * c) `bytesSent` = internal counter of bytes sent
224          *
225          * We have a data reception command when `commandBytes` and `totalBytes` are not equal,
226          * and data transmission when they are equal.
227          *
228          * If we have a transmission command, we send all `totalBytes` to the flash and store
229          * the received data in the receiveFifo.
230          *
231          * If we have a reception command, we first check how many address bytes we have to send,
232          * and then send the command and address bytes; if there are any data bytes in the command,
233          * we can send dummy data to the flash and just store the received data in the receiveFifo.
234          *
235          * If the internal counter `bytesSent` is equal to `totalBytes`, we call the
236          * `FinishTransmission()` method on the registered peripheral.
237          * We can assume that commands will not be interrupted.
238          *
239          */
HandleByte(uint val)240         private void HandleByte(uint val)
241         {
242             if(enabled.Value)
243             {
244                 // reception
245                 if(commandBytes.Value != totalBytes)
246                 {
247                     // 1 command byte
248                     if(commandBytes.Value == 1)
249                     {
250                         HandleByteTransmission(val);
251                         for(var i = bytesSent; i < totalBytes; i++)
252                         {
253                             HandleByteReception();
254                         }
255                     }
256                     // 1 command byte + 3 or 4 address bytes
257                     else
258                     {
259                         if(bytesSent < (int)commandBytes.Value)
260                         {
261                             HandleByteTransmission(val);
262                         }
263                         if(bytesSent == (int)commandBytes.Value)
264                         {
265                             for(var i = bytesSent; i < totalBytes; i++)
266                             {
267                                 HandleByteReception();
268                             }
269                         }
270                     }
271                     TryHandleInterrupt(rxDone);
272                 }
273                 // transmission
274                 else
275                 {
276                     if(bytesSent < totalBytes)
277                     {
278                         HandleByteTransmission(val);
279                     }
280                     TryHandleInterrupt(txDone);
281                 }
282             }
283         }
284 
HandleByteTransmission(uint val)285         private void HandleByteTransmission(uint val)
286         {
287             RegisteredPeripheral.Transmit((byte)val);
288             TryFinishTransmission();
289         }
290 
HandleByteReception()291         private void HandleByteReception()
292         {
293             var receivedByte = RegisteredPeripheral.Transmit(0);
294             if(bytesToSkip > 0)
295             {
296                 bytesToSkip--;
297             }
298             else
299             {
300                 TryReceive(receivedByte);
301             }
302             TryFinishTransmission();
303         }
304 
TryFinishTransmission()305         private void TryFinishTransmission()
306         {
307             bytesSent++;
308             if(bytesSent == totalBytes)
309             {
310                 RegisteredPeripheral.FinishTransmission();
311             }
312         }
313 
TryHandleInterrupt(IFlagRegisterField field)314         private void TryHandleInterrupt(IFlagRegisterField field)
315         {
316             if(bytesSent == totalBytes)
317             {
318                 field.Value = true;
319                 RefreshInterrupt();
320             }
321         }
322 
RefreshInterrupt()323         private void RefreshInterrupt()
324         {
325             var value = false;
326             value |= txDone.Value && txDoneInterruptEnabled.Value;
327             value |= rxDone.Value && rxDoneInterruptEnabled.Value;
328             value |= IsRxAvailable() && rxAvailableInterruptEnabled.Value;
329             value |= txAvailableInterruptEnabled.Value;
330             value |= !IsRxAvailable() && rxFifoEmptyInterruptEnabled.Value;
331 
332             IRQ.Set(value);
333         }
334 
IsRxAvailable()335         private bool IsRxAvailable()
336         {
337             lock(locker)
338             {
339                 return receiveFifo.Count >= (x4Enabled.Value ? 4 : 1);
340             }
341         }
342 
343 
344         private readonly Queue<byte> receiveFifo = new Queue<byte>();
345         private readonly DoubleWordRegisterCollection registers;
346         private readonly IFlagRegisterField enabled;
347         private readonly IFlagRegisterField xipMode;
348         private readonly IValueRegisterField commandBytes;
349         private readonly IFlagRegisterField x4Enabled;
350         private readonly IValueRegisterField upperAddress;
351         private readonly IFlagRegisterField txDone;
352         private readonly IFlagRegisterField rxDone;
353         private readonly IFlagRegisterField txDoneInterruptEnabled;
354         private readonly IFlagRegisterField rxDoneInterruptEnabled;
355         private readonly IFlagRegisterField rxAvailableInterruptEnabled;
356         private readonly IFlagRegisterField txAvailableInterruptEnabled;
357         private readonly IFlagRegisterField rxFifoEmptyInterruptEnabled;
358 
359         private uint totalBytes;
360         private uint bytesToSkip;
361         private uint idleCycles;
362         private int bytesSent;
363 
364         //Registers are aliased every 256 bytes
365         private const int RegisterAliasSize = 256;
366         private const uint MinimumSize = (uint)Registers.FramesUpper + 4;
367         private object locker;
368 
369         private enum XIPAddressBytes
370         {
371             Bytes3 = 0,
372             Bytes4 = 1
373         }
374 
375         private enum Registers
376         {
377             Control = 0x0,
378             Frames = 0x4,
379             //0x8 reserved
380             InterruptEnable = 0xc,
381             Status = 0x10,
382             DirectAccess = 0x14,
383             UpperAddress = 0x18,
384             RxData1 = 0x40,
385             TxData1 = 0x44,
386             RxData4 = 0x48,
387             TxData4 = 0x4c,
388             FramesUpper = 0x50,
389         }
390     }
391 }
392