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 Antmicro.Renode.Core;
10 using Antmicro.Renode.Core.Structure;
11 using Antmicro.Renode.Core.Structure.Registers;
12 using Antmicro.Renode.Logging;
13 using Antmicro.Renode.Peripherals.Bus;
14 using Antmicro.Renode.Utilities;
15 using Antmicro.Renode.Exceptions;
16 
17 namespace Antmicro.Renode.Peripherals.SPI
18 {
19     public class DesignWare_SPI: SimpleContainer<ISPIPeripheral>, IDoubleWordPeripheral, IKnownSize
20     {
DesignWare_SPI(IMachine machine, uint transmitDepth, uint receiveDepth)21         public DesignWare_SPI(IMachine machine, uint transmitDepth, uint receiveDepth) : base(machine)
22         {
23             transmitBuffer = new Queue<ushort>();
24             receiveBuffer = new Queue<ushort>();
25 
26             this.transmitDepth = transmitDepth;
27 
28             var registersMap = new Dictionary<long, DoubleWordRegister>
29             {
30                 {(long)Registers.Control0, new DoubleWordRegister(this, 0x7)
31                     .WithReservedBits(20, 12)
32                     .WithValueField(16, 4, FieldMode.Read, valueProviderCallback: _ => dataFrameSize.Value, name: "DFS_32")
33                     .WithTag("CFS", 12, 4)
34                     .WithTaggedFlag("SRL", 11)
35                     .WithTaggedFlag("SLV_OE", 10)
36                     .WithEnumField<DoubleWordRegister, TransferMode>(8, 2, out transferMode, name: "TMOD")
37                     .WithTaggedFlag("SCPOL", 7)
38                     .WithTaggedFlag("SCPH", 6)
39                     .WithTag("FRF", 4, 2)
40                     .WithValueField(0, 4, out dataFrameSize, name: "DFS", writeCallback: (_, val) =>
41                     {
42                         if(val == 7)
43                         {
44                             FrameSize = TransferSize.SingleByte;
45                         }
46                         else if(val == 15)
47                         {
48                             FrameSize = TransferSize.DoubleByte;
49                         }
50                         else
51                         {
52                             this.Log(LogLevel.Error, "Only 8/16-bit transfers are supported. Falling back to the default 8-bit mode");
53                             dataFrameSize.Value = 7;
54                             FrameSize = TransferSize.SingleByte;
55                         }
56                     })
57                 },
58 
59                 {(long)Registers.Control1, new DoubleWordRegister(this)
60                     .WithReservedBits(16, 16)
61                     .WithValueField(0, 16, out numberOfFrames, name: "NDF")
62                 },
63 
64                 {(long)Registers.Enable, new DoubleWordRegister(this)
65                     .WithReservedBits(1, 31)
66                     .WithFlag(0, out enabled, changeCallback: (_, val) =>
67                     {
68                         if(val == false)
69                         {
70                             ClearBuffers();
71                         }
72                     }, name: "SSI_EN")
73                 },
74 
75                 {(long)Registers.SlaveSelect, new DoubleWordRegister(this)
76                     .WithReservedBits(3, 29)
77                     .WithEnumField<DoubleWordRegister, SlaveSelect>(0, 3, name: "SER", writeCallback: (previousVal, val) =>
78                     {
79                         if(!TryDecodeSlaveId(val, out var newId))
80                         {
81                             return;
82                         }
83 
84                         // no slave selected
85                         if(newId == 0)
86                         {
87                             if(!TryDecodeSlaveId(previousVal, out var oldId))
88                             {
89                                 return;
90                             }
91 
92                             if(!TryGetByAddress(oldId, out var slave))
93                             {
94                                 this.Log(LogLevel.Warning, "Trying to de-select slave #{0} that is not connected", oldId);
95                                 return;
96                             }
97 
98                             slave.FinishTransmission();
99                         }
100                         else
101                         {
102                             TrySendData(newId);
103                         }
104                     })
105                 },
106 
107                 {(long)Registers.ClockDivider, new DoubleWordRegister(this)
108                     .WithValueField(1, 16, name: "SCKDV_15_1")
109                     .WithFlag(0, FieldMode.Read, name: "SCKDV_0") // it's always 0 to ensure that the divider is even
110                 },
111 
112                 {(long)Registers.TransmitTreshold, new DoubleWordRegister(this)
113                     .WithReservedBits(8, 24)
114                     .WithValueField(0, 8, writeCallback: (_, val) =>
115                     {
116                         if(val <= transmitDepth)
117                         {
118                             transmitThreshold = (uint)val;
119                         }
120                         else
121                         {
122                             this.Log(LogLevel.Warning, "Ignored setting transmit threshold to a value (0x{0:X}) greater than the fifo depth (0x{1:X})", val, transmitDepth);
123                         }
124                     }, valueProviderCallback: _ => transmitThreshold, name: "TFT")
125                 },
126 
127                 {(long)Registers.ReceiveTreshold, new DoubleWordRegister(this)
128                     .WithReservedBits(8, 24)
129                     .WithValueField(0, 8, writeCallback: (_, val) =>
130                     {
131                         if(val <= receiveDepth)
132                         {
133                             receiveThreshold = (uint)val;
134                         }
135                         else
136                         {
137                             this.Log(LogLevel.Warning, "Ignored setting receive threshold to a value (0x{0:X}) greater than the fifo depth (0x{1:X})", val, receiveDepth);
138                         }
139                     }, valueProviderCallback: _ => receiveThreshold, name: "RFT")
140                 },
141 
142                 {(long)Registers.TransmitLevel, new DoubleWordRegister(this)
143                     .WithReservedBits(3, 29)
144                     .WithValueField(0, 3, FieldMode.Read, valueProviderCallback: _ => (uint)transmitBuffer.Count, name: "TXFLR")
145                 },
146 
147                 {(long)Registers.ReceiveLevel, new DoubleWordRegister(this)
148                     .WithReservedBits(3, 29)
149                     .WithValueField(0, 3, FieldMode.Read, valueProviderCallback: _ => (uint)receiveBuffer.Count, name: "RXFLR")
150                 },
151 
152                 {(long)Registers.Status, new DoubleWordRegister(this)
153                     .WithReservedBits(7, 25)
154                     .WithTag("DCOL", 6, 1) // read-to-clear
155                     .WithTag("TXE", 5, 1) // read-to-clear, available in slave mode only
156                     .WithFlag(4, FieldMode.Read, valueProviderCallback: _ => receiveBuffer.Count == receiveDepth, name: "RFF")
157                     .WithFlag(3, FieldMode.Read, valueProviderCallback: _ => receiveBuffer.Count != 0, name: "RFNE")
158                     .WithFlag(2, FieldMode.Read, valueProviderCallback: _ => transmitBuffer.Count == 0, name: "TFE")
159                     .WithFlag(1, FieldMode.Read, valueProviderCallback: _ => transmitBuffer.Count < transmitDepth, name: "TFNF")
160                     .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => false, name: "BUSY") // in Renode transfers are instant, so BUSY is always 'false'
161                 },
162 
163                 {(long)Registers.InterruptMask, new DoubleWordRegister(this)
164                     .WithReservedBits(6, 26)
165                     .WithFlag(5, out multiMasterContentionMask, name: "MSTIM")
166                     .WithFlag(4, out receiveFullMask, name: "RXFIM")
167                     .WithFlag(3, out receiveOverflowMask, name: "RXFOIM")
168                     .WithFlag(2, out receiveUnderflowMask, name: "RXUIM")
169                     .WithFlag(1, out transmitOverflowMask, name: "TXOIM")
170                     .WithFlag(0, out transmitEmptyMask, name: "TXEIM")
171                     .WithWriteCallback((_, __) => UpdateInterrupt())
172                 },
173 
174                 {(long)Registers.InterruptStatus, new DoubleWordRegister(this)
175                     .WithReservedBits(6, 26)
176                     .WithFlag(5, mode: FieldMode.Read, valueProviderCallback: _ => multiMasterContention.Value && multiMasterContentionMask.Value, name: "MSTIS")
177                     .WithFlag(4, mode: FieldMode.Read, valueProviderCallback: _ => receiveFull.Value && receiveFullMask.Value, name: "RXFIS")
178                     .WithFlag(3, mode: FieldMode.Read, valueProviderCallback: _ => receiveOverflow.Value && receiveOverflowMask.Value, name: "RXFOIS")
179                     .WithFlag(2, mode: FieldMode.Read, valueProviderCallback: _ => receiveUnderflow.Value && receiveUnderflowMask.Value, name: "RXUIS")
180                     .WithFlag(1, mode: FieldMode.Read, valueProviderCallback: _ => transmitOverflow.Value && transmitOverflowMask.Value, name: "TXOIS")
181                     .WithFlag(0, mode: FieldMode.Read, valueProviderCallback: _ => transmitEmpty.Value && transmitEmptyMask.Value, name: "TXEIS")
182                 },
183 
184                 {(long)Registers.InterruptRawStatus, new DoubleWordRegister(this)
185                     .WithReservedBits(6, 26)
186                     .WithFlag(5, mode: FieldMode.Read, flagField: out multiMasterContention, name: "MSTIR")
187                     .WithFlag(4, mode: FieldMode.Read, flagField: out receiveFull, name: "RXFIR")
188                     .WithFlag(3, mode: FieldMode.Read, flagField: out receiveOverflow, name: "RXFOIR")
189                     .WithFlag(2, mode: FieldMode.Read, flagField: out receiveUnderflow, name: "RXUIR")
190                     .WithFlag(1, mode: FieldMode.Read, flagField: out transmitOverflow, name: "TXOIR")
191                     .WithFlag(0, mode: FieldMode.Read, flagField: out transmitEmpty, name: "TXEIR")
192                 },
193 
194                 {(long)Registers.TransmitOverflowInterruptClear, new DoubleWordRegister(this)
195                     .WithReservedBits(1, 31)
196                     .WithFlag(0, mode: FieldMode.Read, name: "TXOICR", readCallback: (_, __) => { transmitOverflow.Value = false; UpdateInterrupt(); })
197                 },
198 
199                 {(long)Registers.ReceiveOverflowInterruptClear, new DoubleWordRegister(this)
200                     .WithReservedBits(1, 31)
201                     .WithFlag(0, mode: FieldMode.Read, name: "RXOICR", readCallback: (_, __) => { receiveOverflow.Value = false; UpdateInterrupt(); })
202                 },
203 
204                 {(long)Registers.ReceiveUnderflowInterruptClear, new DoubleWordRegister(this)
205                     .WithReservedBits(1, 31)
206                     .WithFlag(0, mode: FieldMode.Read, name: "RXUICR", readCallback: (_, __) => { receiveUnderflow.Value = false; UpdateInterrupt(); })
207                 },
208 
209                 {(long)Registers.MultiMasterContentionInterruptClear, new DoubleWordRegister(this)
210                     .WithReservedBits(1, 31)
211                     .WithFlag(0, mode: FieldMode.Read, name: "MSTICR", readCallback: (_, __) => { multiMasterContention.Value = false; UpdateInterrupt(); })
212                 },
213 
214                 {(long)Registers.InterruptClear, new DoubleWordRegister(this)
215                     .WithReservedBits(1, 31)
216                     .WithFlag(0, mode: FieldMode.Read, readCallback: (_, __) =>
217                     {
218                         transmitOverflow.Value = false;
219                         receiveOverflow.Value = false;
220                         receiveUnderflow.Value = false;
221                         multiMasterContention.Value = false;
222 
223                         UpdateInterrupt();
224                     }, name: "ICR")
225                 },
226 
227                 {(long)Registers.DeviceIdentificationCode, new DoubleWordRegister(this)
228                     .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => 0xFFFFFFFF, name: "IDCODE")
229                 },
230 
231                 {(long)Registers.SynopsisComponentVersion, new DoubleWordRegister(this)
232                     .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => 0x3332332A, name: "SSI_COMP_VERSION")
233                 },
234 
235                 {(long)Registers.DataRegister, new DoubleWordRegister(this)
236                     .WithReservedBits(16, 16)
237                     .WithValueField(0, 16, valueProviderCallback: _ =>
238                     {
239                         if(!enabled.Value)
240                         {
241                             this.Log(LogLevel.Warning, "Trying to read value from a disabled SPI");
242                             return 0;
243                         }
244 
245                         if(!TryDequeueFromReceiveBuffer(out var data))
246                         {
247                             this.Log(LogLevel.Warning, "Trying to read from an empty FIFO");
248                             return 0;
249                         }
250 
251                         return data;
252                     },
253                     writeCallback: (_, val) =>
254                     {
255                         if(!enabled.Value)
256                         {
257                             this.Log(LogLevel.Warning, "Cannot write to SPI buffer while disabled");
258                             return;
259                         }
260 
261                         EnqueueToTransmitBuffer((ushort)val);
262                     }, name: "DR")
263                 },
264             };
265 
266             registersCollection = new DoubleWordRegisterCollection(this, registersMap);
267         }
268 
Register(ISPIPeripheral peripheral, NumberRegistrationPoint<int> registrationPoint)269         public override void Register(ISPIPeripheral peripheral, NumberRegistrationPoint<int> registrationPoint)
270         {
271             if(registrationPoint.Address < 1 || registrationPoint.Address > 3)
272             {
273                 throw new RegistrationException("EOS S3 SPI Master supports 3 slaves at addresses 1, 2, 3");
274             }
275 
276             base.Register(peripheral, registrationPoint);
277         }
278 
Reset()279         public override void Reset()
280         {
281             FrameSize = TransferSize.SingleByte;
282 
283             transmitThreshold = 0;
284             receiveThreshold = 0;
285 
286             ClearBuffers();
287 
288             registersCollection.Reset();
289             UpdateInterrupt();
290         }
291 
ReadDoubleWord(long offset)292         public uint ReadDoubleWord(long offset)
293         {
294             return registersCollection.Read(offset);
295         }
296 
WriteDoubleWord(long offset, uint value)297         public void WriteDoubleWord(long offset, uint value)
298         {
299             registersCollection.Write(offset, value);
300         }
301 
TryDequeueFromReceiveBuffer(out ushort data)302         public bool TryDequeueFromReceiveBuffer(out ushort data)
303         {
304             if(!receiveBuffer.TryDequeue(out data))
305             {
306                 receiveUnderflow.Value = true;
307                 UpdateInterrupt();
308 
309                 data = 0;
310                 return false;
311             }
312 
313             if(receiveBuffer.Count <= receiveThreshold)
314             {
315                 receiveFull.Value = false;
316                 UpdateInterrupt();
317             }
318 
319             return true;
320         }
321 
UpdateInterrupt()322         private void UpdateInterrupt()
323         {
324             var value = false;
325 
326             value |= multiMasterContention.Value && multiMasterContentionMask.Value;
327             value |= receiveFull.Value && receiveFullMask.Value;
328             value |= receiveOverflow.Value && receiveOverflowMask.Value;
329             value |= receiveUnderflow.Value && receiveUnderflowMask.Value;
330             value |= transmitOverflow.Value && transmitOverflowMask.Value;
331             value |= transmitEmpty.Value && transmitEmptyMask.Value;
332 
333             this.Log(LogLevel.Noisy, "Setting IRQ to {0}", value);
334             IRQ.Set(value);
335         }
336 
TrySendData(int slaveAddress)337         private bool TrySendData(int slaveAddress)
338         {
339             if(!enabled.Value)
340             {
341                 this.Log(LogLevel.Warning, "Cannot transmit data while SPI is disabled");
342                 return false;
343             }
344 
345             if(!this.TryGetByAddress(slaveAddress, out var peripheral))
346             {
347                 this.Log(LogLevel.Warning, "Trying to send data to a not attached slave #{0}", slaveAddress);
348                 return false;
349             }
350 
351             this.Log(LogLevel.Noisy, "Transmit mode {0} selected", transferMode.Value);
352 
353             if(transmitBuffer.Count == 0 && transferMode.Value != TransferMode.Receive)
354             {
355                 this.Log(LogLevel.Warning, "No data to transmit");
356                 return false;
357             }
358 
359             var bytesFromFrames = ((int)numberOfFrames.Value + 1) * (FrameSize == TransferSize.SingleByte ? 1 : 2 /* TransferSize.DoubleByte */);
360             switch(transferMode.Value)
361             {
362                 case TransferMode.TransmitReceive:
363                     DoTransfer(peripheral, transmitBuffer.Count, readFromFifo: true, writeToFifo: true);
364                     break;
365                 case TransferMode.Transmit:
366                     DoTransfer(peripheral, transmitBuffer.Count, readFromFifo: true, writeToFifo: false);
367                     break;
368                 case TransferMode.Receive:
369                     DoTransfer(peripheral, bytesFromFrames, readFromFifo: false, writeToFifo: true);
370                     break;
371                 case TransferMode.EEPROM:
372                     // control bytes
373                     DoTransfer(peripheral, transmitBuffer.Count, readFromFifo: true, writeToFifo: false);
374                     // data bytes
375                     DoTransfer(peripheral, bytesFromFrames, readFromFifo: false, writeToFifo: true);
376                     break;
377             }
378 
379             return true;
380         }
381 
DoTransfer(ISPIPeripheral peripheral, int size, bool readFromFifo, bool writeToFifo)382         private void DoTransfer(ISPIPeripheral peripheral, int size, bool readFromFifo, bool writeToFifo)
383         {
384             this.Log(LogLevel.Noisy, "Doing an SPI transfer of size {0} bytes (reading from fifo: {1}, writing to fifo: {2})", size, readFromFifo, writeToFifo);
385             for(var i = 0; i < size; i++)
386             {
387                 ushort dataFromSlave = 0;
388                 var dataToSlave = readFromFifo ? transmitBuffer.Dequeue() : (ushort)0;
389                 switch(FrameSize)
390                 {
391                     case TransferSize.SingleByte:
392                         dataFromSlave = peripheral.Transmit((byte)dataToSlave);
393                         break;
394 
395                     case TransferSize.DoubleByte:
396                     {
397                         var responseHigh = peripheral.Transmit((byte)(dataToSlave >> 8));
398                         var responseLow = peripheral.Transmit((byte)dataToSlave);
399                         dataFromSlave = (ushort)((responseHigh << 8) | responseLow);
400                         break;
401                     }
402 
403                     default:
404                         throw new ArgumentException($"Unexpected transfer size {FrameSize}");
405                 }
406 
407                 this.Log(LogLevel.Noisy, "Sent 0x{0:X}, received 0x{1:X}", dataToSlave, dataFromSlave);
408 
409                 if(!writeToFifo)
410                 {
411                     continue;
412                 }
413 
414                 lock(innerLock)
415                 {
416                     receiveBuffer.Enqueue(dataFromSlave);
417                     if(receiveBuffer.Count > receiveThreshold)
418                     {
419                         receiveFull.Value = true;
420                         UpdateInterrupt();
421                     }
422                 }
423             }
424         }
425 
EnqueueToTransmitBuffer(ushort val)426         private void EnqueueToTransmitBuffer(ushort val)
427         {
428             if(transmitBuffer.Count == transmitDepth)
429             {
430                 this.Log(LogLevel.Warning, "Trying to write to a full FIFO. Dropping the data");
431                 transmitOverflow.Value = true;
432                 UpdateInterrupt();
433                 return;
434             }
435 
436             transmitBuffer.Enqueue(val);
437 
438             if(transmitBuffer.Count <= transmitThreshold)
439             {
440                 transmitEmpty.Value = true;
441                 UpdateInterrupt();
442             }
443         }
444 
TryDecodeSlaveId(SlaveSelect val, out int id)445         private bool TryDecodeSlaveId(SlaveSelect val, out int id)
446         {
447             switch(val)
448             {
449                 case SlaveSelect.Slave1:
450                     id = 1;
451                     return true;
452                 case SlaveSelect.Slave2:
453                     id = 2;
454                     return true;
455                 case SlaveSelect.Slave3:
456                     id = 3;
457                     return true;
458                 case SlaveSelect.None:
459                     id = 0;
460                     return true;
461                 default:
462                     this.Log(LogLevel.Warning, "Unexpected Slave Select (SER) value: 0x{0:X}", val);
463                     id = -1;
464                     return false;
465             }
466         }
467 
ClearBuffers()468         private void ClearBuffers()
469         {
470             lock(innerLock)
471             {
472                 receiveBuffer.DequeueAll();
473                 transmitBuffer.DequeueAll();
474 
475                 transmitEmpty.Value = true;
476                 receiveFull.Value = false;
477                 UpdateInterrupt();
478             }
479         }
480 
481         public long Size => 0x400;
482 
483         public GPIO IRQ { get; private set; } = new GPIO();
484 
485         public TransferSize FrameSize { get; private set; }
486 
487         private uint transmitThreshold;
488         private uint receiveThreshold;
489 
490         private IValueRegisterField dataFrameSize;
491         private IEnumRegisterField<TransferMode> transferMode;
492         private IValueRegisterField numberOfFrames;
493         private IFlagRegisterField enabled;
494 
495         private IFlagRegisterField multiMasterContentionMask;
496         private IFlagRegisterField receiveFullMask;
497         private IFlagRegisterField receiveOverflowMask;
498         private IFlagRegisterField receiveUnderflowMask;
499         private IFlagRegisterField transmitOverflowMask;
500         private IFlagRegisterField transmitEmptyMask;
501 
502         private IFlagRegisterField multiMasterContention; // this IRQ is never set in the current implementation
503         private IFlagRegisterField receiveFull;
504         private IFlagRegisterField receiveOverflow;
505         private IFlagRegisterField receiveUnderflow;
506         private IFlagRegisterField transmitOverflow;
507         private IFlagRegisterField transmitEmpty;
508 
509         private readonly uint transmitDepth;
510 
511         // a single frame can have up to 16-bits
512         private readonly Queue<ushort> receiveBuffer;
513         private readonly Queue<ushort> transmitBuffer;
514 
515         private readonly DoubleWordRegisterCollection registersCollection;
516         private readonly object innerLock = new object();
517 
518         public enum TransferSize
519         {
520             SingleByte = 0,
521             DoubleByte = 1
522         }
523 
524         private enum TransferMode
525         {
526             TransmitReceive = 0x0,
527             Transmit = 0x1,
528             Receive = 0x2,
529             EEPROM = 0x3,
530         }
531 
532         private enum SlaveSelect
533         {
534             None = 0,
535             Slave1 = 1,
536             Slave2 = 2,
537             Slave3 = 4
538         }
539 
540         private enum Registers
541         {
542             Control0 = 0x0,
543             Control1 = 0x4,
544             Enable = 0x8,
545             SlaveSelect = 0x10,
546             ClockDivider = 0x14,
547             TransmitTreshold = 0x18,
548             ReceiveTreshold = 0x1C,
549             TransmitLevel = 0x20,
550             ReceiveLevel = 0x24,
551             Status = 0x28,
552             InterruptMask = 0x2C,
553             InterruptStatus = 0x30,
554             InterruptRawStatus = 0x34,
555             TransmitOverflowInterruptClear = 0x38,
556             ReceiveOverflowInterruptClear = 0x3C,
557             ReceiveUnderflowInterruptClear = 0x40,
558             MultiMasterContentionInterruptClear = 0x44,
559             InterruptClear = 0x48,
560             DeviceIdentificationCode = 0x58,
561             SynopsisComponentVersion = 0x5C,
562             DataRegister = 0x60,
563         }
564     }
565 }
566