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.Collections.Generic;
8 using System.Linq;
9 using Antmicro.Renode.Peripherals.Bus;
10 using Antmicro.Renode.Peripherals.Helpers;
11 using Antmicro.Renode.Core;
12 using Antmicro.Renode.Core.Structure;
13 using Antmicro.Renode.Core.Structure.Registers;
14 using Antmicro.Renode.Logging;
15 using Antmicro.Renode.Utilities;
16 
17 namespace Antmicro.Renode.Peripherals.SPI
18 {
19     [AllowedTranslations(AllowedTranslation.ByteToDoubleWord | AllowedTranslation.WordToDoubleWord)]
20     public class Cadence_SPI : SimpleContainer<ISPIPeripheral>, IDoubleWordPeripheral, IKnownSize
21     {
Cadence_SPI(IMachine machine, int txFifoCapacity = DefaultTxFifoCapacity, int rxFifoCapacity = DefaultRxFifoCapacity)22         public Cadence_SPI(IMachine machine, int txFifoCapacity = DefaultTxFifoCapacity, int rxFifoCapacity = DefaultRxFifoCapacity) : base(machine)
23         {
24             this.txFifoCapacity = txFifoCapacity;
25             this.rxFifoCapacity = rxFifoCapacity;
26 
27             txFifo = new Queue<byte>(this.txFifoCapacity);
28             rxFifo = new Queue<byte>(this.rxFifoCapacity);
29             registers = new DoubleWordRegisterCollection(this, BuildRegisterMap());
30 
31             txFifoFull = new CadenceInterruptFlag(() => txFifo.Count >= this.txFifoCapacity);
32             txFifoNotFull = new CadenceInterruptFlag(() => txFifo.Count < (int)txFifoThreshold.Value);
33             txFifoUnderflow = new CadenceInterruptFlag(() => false);
34             rxFifoOverflow = new CadenceInterruptFlag(() => false);
35             rxFifoFull = new CadenceInterruptFlag(() => rxFifo.Count >= this.rxFifoCapacity);
36             rxFifoNotEmpty = new CadenceInterruptFlag(() => rxFifo.Count >= (int)rxFifoThreshold.Value);
37             modeFail = new CadenceInterruptFlag(() => false); // Not handled
38         }
39 
WriteDoubleWord(long offset, uint value)40         public void WriteDoubleWord(long offset, uint value)
41         {
42             registers.Write(offset, value);
43         }
44 
ReadDoubleWord(long offset)45         public uint ReadDoubleWord(long offset)
46         {
47             return registers.Read(offset);
48         }
49 
Reset()50         public override void Reset()
51         {
52             registers.Reset();
53             txFifo.Clear();
54             rxFifo.Clear();
55             selectedPeripheral = null;
56 
57             foreach(var flag in GetInterruptFlags())
58             {
59                 flag.Reset();
60             }
61             UpdateInterrupts();
62         }
63 
64         public long Size => 0x100;
65 
66         [DefaultInterrupt]
67         public GPIO IRQ { get; } = new GPIO();
68 
69         public GPIO TxFifoFullIRQ { get; } = new GPIO();
70         public GPIO TxFifoFillLevelThresholdIRQ { get; } = new GPIO();
71 
72         public GPIO RxFifoFullIRQ { get; } = new GPIO();
73         public GPIO RxFifoFillLevelThresholdIRQ { get; } = new GPIO();
74 
TransmitData()75         private void TransmitData()
76         {
77             if(spiMode.Value == SPIMode.PeripheralMode)
78             {
79                 this.Log(LogLevel.Warning, "Trying to transfer data from a SPI in peripheral mode. Only controller mode is supported.");
80                 return;
81             }
82             if(!spiEnable.Value)
83             {
84                 this.Log(LogLevel.Warning, "Trying to transfer data from a disabled SPI.");
85                 return;
86             }
87             if(txFifo.Count == 0)
88             {
89                 txFifoUnderflow.SetSticky(true);
90                 this.Log(LogLevel.Debug, "Trying to transfer data from an empty Tx FIFO.");
91                 return;
92             }
93 
94             selectedPeripheral = GetPeripheral();
95             if(selectedPeripheral != null)
96             {
97                 while(txFifo.Count > 0)
98                 {
99                     EnqueueRx(selectedPeripheral.Transmit(txFifo.Dequeue()));
100                 }
101                 if(!manualChipSelect.Value)
102                 {
103                     DeselectPeripheral();
104                 }
105             }
106             else
107             {
108                 for(var i = 0; i < txFifo.Count; i++)
109                 {
110                     EnqueueRx(default(byte));
111                 }
112                 txFifo.Clear();
113                 this.Log(LogLevel.Warning, "There is no peripheral with selected address, received data contains dummy bytes.");
114             }
115         }
116 
TryTransmitDataAutomatically()117         private void TryTransmitDataAutomatically()
118         {
119             if(!manualTransmission.Value && txFifo.Count > 0)
120             {
121                 TransmitData();
122             }
123         }
124 
DeselectPeripheral()125         private void DeselectPeripheral()
126         {
127             if(selectedPeripheral != null)
128             {
129                 selectedPeripheral.FinishTransmission();
130                 selectedPeripheral = null;
131             }
132         }
133 
EnqueueTx(byte data)134         private void EnqueueTx(byte data)
135         {
136             if(!spiEnable.Value)
137             {
138                 this.Log(LogLevel.Debug, "Writing to a disabled SPI peripheral FIFO.");
139             }
140             if(txFifoFull.Status)
141             {
142                 this.Log(LogLevel.Warning, "Trying to write to a full Tx FIFO, data not queued.");
143                 // According to the Zynq US+ Technical Reference Manual, the rxFifoOverflow interrupt should be asserted also for Tx FIFO overflow.
144                 rxFifoOverflow.SetSticky(true);
145                 return;
146             }
147             txFifo.Enqueue(data);
148         }
149 
EnqueueRx(byte data)150         private void EnqueueRx(byte data)
151         {
152             if(rxFifoFull.Status)
153             {
154                 rxFifoOverflow.SetSticky(true);
155                 this.Log(LogLevel.Warning, "Can't write to a full Rx FIFO, data not queued.");
156                 return;
157             }
158             rxFifo.Enqueue(data);
159         }
160 
DequeueRx()161         private byte DequeueRx()
162         {
163             if(!spiEnable.Value)
164             {
165                 this.Log(LogLevel.Debug, "Reading from a disabled SPI peripheral FIFO.");
166             }
167             if(rxFifo.Count == 0)
168             {
169                 this.Log(LogLevel.Warning, "Trying to read from an empty Rx FIFO, dummy data returned.");
170                 return default(byte);
171             }
172             return rxFifo.Dequeue();
173         }
174 
GetPeripheral()175         private ISPIPeripheral GetPeripheral()
176         {
177             if(TryGetPeripheralAddress(out var address))
178             {
179                 if(TryGetByAddress(address, out var peripheral))
180                 {
181                     return peripheral;
182                 }
183                 this.Log(LogLevel.Warning, "Can't find SPI peripheral at address 0x{0:X}.", address);
184             }
185             return null;
186         }
187 
TryGetPeripheralAddress(out int address)188         private bool TryGetPeripheralAddress(out int address)
189         {
190             if(chipSelectAddr.Value != ChipSelectNoPeripheral)
191             {
192                 var addressReg = (int)chipSelectAddr.Value & ChipSelectAddrMask;
193                 if(chipSelectMode.Value != ChipSelectMode.ThreeChipsNoDecoder)
194                 {
195                     address = addressReg;
196                     return true;
197                 }
198 
199                 // Address is set to 0 for register = 0bxxx0, 1 for register = 0bxx01 and so on
200                 for(var index = 0; index < ChipSelectNoDecoderCount; index++)
201                 {
202                     if((addressReg & 0b1) == 0)
203                     {
204                         address = index;
205                         return true;
206                     }
207                     addressReg >>= 1;
208                 }
209                 // The 0b0111 value of register is reserved
210                 this.Log(LogLevel.Warning, "The 0x{0:X} value of the chipSelectAddr register is reserved.", chipSelectAddr.Value);
211             }
212             address = -1;
213             return false;
214         }
215 
UpdateSticky()216         private void UpdateSticky()
217         {
218             foreach(var flag in GetInterruptFlags())
219             {
220                 flag.UpdateStickyStatus();
221             }
222         }
223 
UpdateInterrupts()224         private void UpdateInterrupts()
225         {
226             IRQ.Set(GetInterruptFlags().Any(x => x.InterruptStatus));
227             RxFifoFullIRQ.Set(rxFifoFull.InterruptStatus);
228             TxFifoFullIRQ.Set(txFifoFull.InterruptStatus);
229             RxFifoFillLevelThresholdIRQ.Set(rxFifoNotEmpty.InterruptStatus);
230             TxFifoFillLevelThresholdIRQ.Set(txFifoNotFull.InterruptStatus);
231         }
232 
BuildRegisterMap()233         private Dictionary<long, DoubleWordRegister> BuildRegisterMap()
234         {
235             var txFifoThresholdBits = BitHelper.GetMostSignificantSetBitIndex((ulong)txFifoCapacity);
236             var rxFifoThresholdBits = BitHelper.GetMostSignificantSetBitIndex((ulong)rxFifoCapacity);
237             return new Dictionary<long, DoubleWordRegister>
238             {
239                 {(long)Registers.Config, new DoubleWordRegister(this, 0x00020000)
240                     .WithReservedBits(18, 14)
241                     .WithFlag(17, out modeFailEnable, name: "modeFailEnable")
242                     .WithFlag(16, FieldMode.Write, name: "manualStartTransmission",
243                         writeCallback: (_, val) => { if(val) TransmitData(); })
244                     .WithFlag(15, out manualTransmission, name: "manualTransmission")
245                     .WithFlag(14, out manualChipSelect, name: "manualChipSelect")
246                     .WithValueField(10, 4, out chipSelectAddr, name: "chipSelectAddress")
247                     .WithEnumField(9, 1, out chipSelectMode)
248                     .WithFlag(8, out referenceClockSelect, name: "referenceClockSelect")
249                     .WithReservedBits(6, 2)
250                     .WithValueField(3, 3, out baudRateDivider, name: "baudRateDivider")
251                     .WithFlag(2, out clockPhase, name: "clockPhase")
252                     .WithFlag(1, out clockPolarity, name: "clockPolarity")
253                     .WithEnumField(0, 1, out spiMode, name: "spiMode",
254                         changeCallback: (_, val) => {
255                             if(val == SPIMode.PeripheralMode)
256                             {
257                                 this.Log(LogLevel.Warning, "SPI mode changed to peripheral, while only controller mode is supported.");
258                             }
259                         })
260                     .WithWriteCallback((_, val) =>
261                     {
262                         if(chipSelectAddr.Value == ChipSelectNoPeripheral || selectedPeripheral != GetPeripheral())
263                         {
264                             DeselectPeripheral();
265                         }
266                     })
267                 },
268                 {(long)Registers.InterruptStatus, new DoubleWordRegister(this)
269                     .WithReservedBits(7, 25)
270                     .WithFlag(6,
271                         valueProviderCallback: (_) => txFifoUnderflow.StickyStatus,
272                         writeCallback: (_, val) => txFifoUnderflow.ClearSticky(val),
273                         name: "txFifoUnderflowInterruptStatus"
274                     )
275                     // A few interrupts aren't sticky so they are cleared at every read
276                     .WithFlag(5,
277                         valueProviderCallback: (_) => rxFifoFull.StickyStatus,
278                         writeCallback: (_, val) => rxFifoFull.ClearSticky(val),
279                         readCallback:  (_, __) => rxFifoFull.ClearSticky(true),
280                         name: "rxFifoFullInterruptStatus"
281                     )
282                     .WithFlag(4,
283                         valueProviderCallback: (_) => rxFifoNotEmpty.StickyStatus,
284                         writeCallback: (_, val) => rxFifoNotEmpty.ClearSticky(val),
285                         readCallback:  (_, __) => rxFifoNotEmpty.ClearSticky(true),
286                         name: "rxFifoNotEmptyInterruptStatus"
287                     )
288                     .WithFlag(3,
289                         valueProviderCallback: (_) => txFifoFull.StickyStatus,
290                         writeCallback: (_, val) => txFifoFull.ClearSticky(val),
291                         readCallback:  (_, __) => txFifoFull.ClearSticky(true),
292                         name: "txFifoFullInterruptStatus"
293                     )
294                     .WithFlag(2,
295                         valueProviderCallback: (_) => txFifoNotFull.StickyStatus,
296                         writeCallback: (_, val) => txFifoNotFull.ClearSticky(val),
297                         readCallback:  (_, __) => txFifoNotFull.ClearSticky(true),
298                         name: "txFifoNotFullInterruptStatus"
299                     )
300                     .WithFlag(1,
301                         valueProviderCallback: (_) => modeFail.StickyStatus,
302                         writeCallback: (_, val) => modeFail.ClearSticky(val),
303                         name: "modeFailInterruptStatus"
304                     )
305                     .WithFlag(0,
306                         valueProviderCallback: (_) => rxFifoOverflow.StickyStatus,
307                         writeCallback: (_, val) => rxFifoOverflow.ClearSticky(val),
308                         name: "rxFifoOverflowInterruptStatus"
309                     )
310                     .WithWriteCallback((_, __) =>
311                     {
312                         UpdateSticky();
313                         UpdateInterrupts();
314                     })
315                 },
316                 {(long)Registers.InterruptEnable, new DoubleWordRegister(this)
317                     .WithReservedBits(7, 25)
318                     .WithFlag(6, FieldMode.Write,
319                         writeCallback: (_, val) => txFifoUnderflow.InterruptEnable(val),
320                         name: "txFifoUnderflowInterruptEnable"
321                     )
322                     .WithFlag(5, FieldMode.Write,
323                         writeCallback: (_, val) => rxFifoFull.InterruptEnable(val),
324                         name: "rxFifoFullInterruptEnable"
325                     )
326                     .WithFlag(4, FieldMode.Write,
327                         writeCallback: (_, val) => rxFifoNotEmpty.InterruptEnable(val),
328                         name: "rxFifoNotEmptyInterruptEnable"
329                     )
330                     .WithFlag(3, FieldMode.Write,
331                         writeCallback: (_, val) => txFifoFull.InterruptEnable(val),
332                         name: "txFifoFullInterruptEnable"
333                     )
334                     .WithFlag(2, FieldMode.Write,
335                         writeCallback: (_, val) => txFifoNotFull.InterruptEnable(val),
336                         name: "txFifoNotFullInterruptEnable"
337                     )
338                     .WithFlag(1, FieldMode.Write,
339                         writeCallback: (_, val) => modeFail.InterruptEnable(val),
340                         name: "modeFailInterruptEnable"
341                     )
342                     .WithFlag(0, FieldMode.Write,
343                         writeCallback: (_, val) => rxFifoOverflow.InterruptEnable(val),
344                         name: "rxFifoOverflowInterruptEnable"
345                     )
346                     .WithWriteCallback((_, __) => UpdateInterrupts())
347                 },
348                 {(long)Registers.InterruptDisable, new DoubleWordRegister(this)
349                     .WithReservedBits(7, 25)
350                     .WithFlag(6, FieldMode.Write,
351                         writeCallback: (_, val) => txFifoUnderflow.InterruptDisable(val),
352                         name: "txFifoUnderflowInterruptDisable"
353                     )
354                     .WithFlag(5, FieldMode.Write,
355                         writeCallback: (_, val) => rxFifoFull.InterruptDisable(val),
356                         name: "rxFifoFullInterruptDisable"
357                     )
358                     .WithFlag(4, FieldMode.Write,
359                         writeCallback: (_, val) => rxFifoNotEmpty.InterruptDisable(val),
360                         name: "rxFifoNotEmptyInterruptDisable"
361                     )
362                     .WithFlag(3, FieldMode.Write,
363                         writeCallback: (_, val) => txFifoFull.InterruptDisable(val),
364                         name: "txFifoFullInterruptDisable"
365                     )
366                     .WithFlag(2, FieldMode.Write,
367                         writeCallback: (_, val) => txFifoNotFull.InterruptDisable(val),
368                         name: "txFifoNotFullInterruptDisable"
369                     )
370                     .WithFlag(1, FieldMode.Write,
371                         writeCallback: (_, val) => modeFail.InterruptDisable(val),
372                         name: "modeFailInterruptDisable"
373                     )
374                     .WithFlag(0, FieldMode.Write,
375                         writeCallback: (_, val) => rxFifoOverflow.InterruptDisable(val),
376                         name: "rxFifoOverflowInterruptDisable"
377                     )
378                     .WithWriteCallback((_, __) => UpdateInterrupts())
379                 },
380                 {(long)Registers.InterruptMask, new DoubleWordRegister(this)
381                     .WithReservedBits(7, 25)
382                     .WithFlag(6, FieldMode.Read,
383                         valueProviderCallback: (_) => txFifoUnderflow.InterruptMask,
384                         name: "txFifoUnderflowInterruptMask"
385                     )
386                     .WithFlag(5, FieldMode.Read,
387                         valueProviderCallback: (_) => rxFifoFull.InterruptMask,
388                         name: "rxFifoFullInterruptMask"
389                     )
390                     .WithFlag(4, FieldMode.Read,
391                         valueProviderCallback: (_) => rxFifoNotEmpty.InterruptMask,
392                         name: "rxFifoNotEmptyInterruptMask"
393                     )
394                     .WithFlag(3, FieldMode.Read,
395                         valueProviderCallback: (_) => txFifoFull.InterruptMask,
396                         name: "txFifoFullInterruptMask"
397                     )
398                     .WithFlag(2, FieldMode.Read,
399                         valueProviderCallback: (_) => txFifoNotFull.InterruptMask,
400                         name: "txFifoNotFullInterruptMask"
401                     )
402                     .WithFlag(1, FieldMode.Read,
403                         valueProviderCallback: (_) => modeFail.InterruptMask,
404                         name: "modeFailInterruptMask"
405                     )
406                     .WithFlag(0, FieldMode.Read,
407                         valueProviderCallback: (_) => rxFifoOverflow.InterruptMask,
408                         name: "rxFifoOverflowInterruptMask"
409                     )
410                 },
411                 {(long)Registers.Enable, new DoubleWordRegister(this)
412                     .WithReservedBits(1, 31)
413                     .WithFlag(0, out spiEnable, name: "spiEnable",
414                         writeCallback: (_, val) =>
415                         {
416                             if(val)
417                             {
418                                 TryTransmitDataAutomatically();
419                             }
420                             else
421                             {
422                                 DeselectPeripheral();
423                             }
424                         })
425                 },
426                 {(long)Registers.TxData, new DoubleWordRegister(this)
427                     .WithReservedBits(8, 24)
428                     .WithValueField(0, 8, FieldMode.Write, name: "txData",
429                         writeCallback: (_, data) =>
430                         {
431                             EnqueueTx((byte)data);
432                             TryTransmitDataAutomatically();
433                         })
434                     .WithWriteCallback((_, __) =>
435                     {
436                         UpdateSticky();
437                         UpdateInterrupts();
438                     })
439                 },
440                 {(long)Registers.RxData, new DoubleWordRegister(this)
441                     .WithReservedBits(8, 24)
442                     .WithValueField(0, 8, FieldMode.Read, name: "rxData",
443                         valueProviderCallback: (_) => DequeueRx()
444                     )
445                     .WithReadCallback((_, __) =>
446                     {
447                         UpdateSticky();
448                         UpdateInterrupts();
449                     })
450                 },
451                 {(long)Registers.TxFifoThreshold, new DoubleWordRegister(this, InitialFifoThreshold)
452                     .WithReservedBits(txFifoThresholdBits, 32 - txFifoThresholdBits)
453                     .WithValueField(0, txFifoThresholdBits, out txFifoThreshold)
454                     .WithWriteCallback((_, __) =>
455                     {
456                         UpdateSticky();
457                         UpdateInterrupts();
458                     })
459                 },
460                 {(long)Registers.RxFifoThreshold, new DoubleWordRegister(this, InitialFifoThreshold)
461                     .WithReservedBits(rxFifoThresholdBits, 32 - rxFifoThresholdBits)
462                     .WithValueField(0, rxFifoThresholdBits, out rxFifoThreshold)
463                     .WithWriteCallback((_, __) =>
464                     {
465                         UpdateSticky();
466                         UpdateInterrupts();
467                     })
468                 },
469                 {(long)Registers.ModuleID, new DoubleWordRegister(this)
470                     .WithReservedBits(25, 7)
471                     .WithValueField(0, 25, FieldMode.Read, name: "moduleID",
472                         valueProviderCallback: (_) => ModuleID
473                     )
474                 }
475             };
476         }
477 
GetInterruptFlags()478         private IEnumerable<CadenceInterruptFlag> GetInterruptFlags()
479         {
480             yield return txFifoFull;
481             yield return txFifoNotFull;
482             yield return txFifoUnderflow;
483             yield return rxFifoOverflow;
484             yield return rxFifoFull;
485             yield return rxFifoNotEmpty;
486         }
487 
488         private ISPIPeripheral selectedPeripheral;
489 
490         private IFlagRegisterField modeFailEnable;
491         private IFlagRegisterField manualTransmission;
492         private IFlagRegisterField manualChipSelect;
493         private IValueRegisterField chipSelectAddr;
494         private IEnumRegisterField<ChipSelectMode> chipSelectMode;
495         private IEnumRegisterField<SPIMode> spiMode;
496         private IFlagRegisterField spiEnable;
497         private IValueRegisterField txFifoThreshold;
498         private IValueRegisterField rxFifoThreshold;
499 
500         // These fields are related to a physical layer, which Renode doesn't simulate for the SPI bus
501         private IFlagRegisterField referenceClockSelect;
502         private IValueRegisterField baudRateDivider;
503         private IFlagRegisterField clockPhase;
504         private IFlagRegisterField clockPolarity;
505 
506         private readonly CadenceInterruptFlag txFifoFull;
507         private readonly CadenceInterruptFlag txFifoNotFull;
508         private readonly CadenceInterruptFlag txFifoUnderflow;
509         private readonly CadenceInterruptFlag rxFifoOverflow;
510         private readonly CadenceInterruptFlag rxFifoFull;
511         private readonly CadenceInterruptFlag rxFifoNotEmpty;
512         private readonly CadenceInterruptFlag modeFail;
513 
514         private readonly int txFifoCapacity;
515         private readonly int rxFifoCapacity;
516         private readonly Queue<byte> txFifo;
517         private readonly Queue<byte> rxFifo;
518         private readonly DoubleWordRegisterCollection registers;
519 
520         private const int DefaultTxFifoCapacity = 128;
521         private const int DefaultRxFifoCapacity = 128;
522         private const int InitialFifoThreshold = 0x1;
523         private const int ChipSelectNoPeripheral = 0b1111;
524         private const int ChipSelectAddrMask = 0b0111;
525         private const int ChipSelectNoDecoderCount = 3;
526         private const int ModuleID = 0x90108;
527 
528         private enum ChipSelectMode
529         {
530             ThreeChipsNoDecoder = 0x0,
531             EightChipsWithDecoder = 0x1
532         }
533 
534         private enum SPIMode
535         {
536             PeripheralMode = 0x0,
537             ControllerMode = 0x1
538         }
539 
540         private enum Registers : long
541         {
542             Config = 0x00,
543             InterruptStatus = 0x04,
544             InterruptEnable = 0x08,
545             InterruptDisable = 0x0c,
546             InterruptMask = 0x10,
547             Enable = 0x14,
548             Delay = 0x18,
549             TxData = 0x1c,
550             RxData = 0x20,
551             SlaveIdleCount = 0x24,
552             TxFifoThreshold = 0x28,
553             RxFifoThreshold = 0x2c,
554             ModuleID = 0xfc
555         }
556     }
557 }
558