1 //
2 // Copyright (c) 2010-2024 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 using System;
8 using System.Collections.Generic;
9 using 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.Peripherals.DMA;
15 using Antmicro.Renode.Utilities;
16 
17 namespace Antmicro.Renode.Peripherals.SPI
18 {
19     public class SAM_SPI : SimpleContainer<ISPIPeripheral>, IDoubleWordPeripheral, IKnownSize, IProvidesRegisterCollection<DoubleWordRegisterCollection>, ISamPdcBytePeripheral, ISamPdcWordPeripheral, ISamPdcDoubleWordPeripheral
20     {
SAM_SPI(IMachine machine)21         public SAM_SPI(IMachine machine) : base(machine)
22         {
23             IRQ = new GPIO();
24             irqManager = new InterruptManager<Interrupts>(this, IRQ, nameof(IRQ));
25             transmitBuffer = new Queue<byte>();
26             RegistersCollection = new DoubleWordRegisterCollection(this);
27             pdc = new SAM_PDC(machine, this, (long)Registers.PdcReceivePointer, HandlePDCInterrupts);
28             DefineRegisters();
29             Reset();
30         }
31 
Reset()32         public override void Reset()
33         {
34             RegistersCollection.Reset();
35             SWReset();
36             irqManager.Reset();
37             irqManager.SetInterrupt(Interrupts.TransmissionRegistersEmpty);
38             pdc.Reset();
39         }
40 
ReadDoubleWord(long offset)41         public uint ReadDoubleWord(long offset)
42         {
43             return RegistersCollection.Read(offset);
44         }
45 
WriteDoubleWord(long offset, uint value)46         public void WriteDoubleWord(long offset, uint value)
47         {
48             if(writeProtection && writeProtected.Contains(offset))
49             {
50                 writeProtectionViolationSource.Value = (ulong)offset;
51                 writeProtectionViolationStatus.Value = true;
52                 this.WarningLog("Writing to Write Protected register at offset: 0x{0:X}", offset);
53             }
54             else
55             {
56                 RegistersCollection.Write(offset, value);
57             }
58         }
59 
DmaByteRead()60         public byte? DmaByteRead() => (byte?)DmaWordRead();
61 
62         public void DmaByteWrite(byte data) => DmaDoubleWordWrite(data);
63 
DmaWordRead()64         public ushort? DmaWordRead()
65         {
66             if(!irqManager.IsSet(Interrupts.ReceiveDataRegisterFull))
67             {
68                 return null;
69             }
70             var rval = RegistersCollection.Read((long)Registers.ReceiveData);
71             return (ushort)(rval & 0xFFFF);
72         }
73 
DmaWordWrite(ushort data)74         public void DmaWordWrite(ushort data) => DmaDoubleWordWrite(data);
75 
DmaDoubleWordRead()76         public uint? DmaDoubleWordRead() => null;
77 
DmaDoubleWordWrite(uint data)78         public void DmaDoubleWordWrite(uint data)
79         {
80             RegistersCollection.Write((long)Registers.TransmitData, data);
81             pdc.TriggerReceiver();
82         }
83 
84         public DoubleWordRegisterCollection RegistersCollection { get; }
85         public GPIO IRQ { get; }
86         public long Size => 0x128;
87 
88         public TransferType DmaWriteAccessWidth { get; private set; }
89         public TransferType DmaReadAccessWidth { get; private set; }
90 
DefineRegisters()91         private void DefineRegisters()
92         {
93             Registers.Control.Define(this)
94                 .WithFlag(0, FieldMode.Write,
95                     writeCallback: (_, val) =>
96                     {
97                         if(!val)
98                         {
99                             return;
100                         }
101                         isEnabled.Value = true;
102                         irqManager.SetInterrupt(Interrupts.TransmitDataRegisterEmpty);
103                         if(!waitDataReadBeforeTransfer.Value || !irqManager.IsSet(Interrupts.ReceiveDataRegisterFull))
104                         {
105                             TryTransfer();
106                         }
107                     },
108                     name: "SPIEN")
109                 .WithFlag(1, FieldMode.Write,
110                     writeCallback: (_, val) =>
111                     {
112                         if(val)
113                         {
114                             isEnabled.Value = false;
115                             irqManager.ClearInterrupt(Interrupts.TransmitDataRegisterEmpty);
116                         }
117                     },
118                     name: "SPIDIS")
119                 .WithReservedBits(2, 5)
120                 .WithFlag(7, FieldMode.Write,
121                     writeCallback: (_, val) =>
122                     {
123                         if (val)
124                         {
125                             SWReset();
126                         }
127                     },
128                     name: "SWRST")
129                 .WithReservedBits(8, 16)
130                 .WithTaggedFlag("LASTXFER", 24)
131                 .WithReservedBits(25, 7)
132             ;
133 
134             // Protected by WPEN
135             Registers.Mode.Define(this)
136                 .WithEnumField(0, 1, out mode, name: "MSTR")
137                 .WithEnumField(1, 1, out peripheralSelectMode, name: "PS")
138                 .WithFlag(2, out useDecoder, name: "PCSDEC")
139                 .WithReservedBits(3, 1)
140                 .WithTaggedFlag("MODFDIS", 4)
141                 .WithFlag(5, out waitDataReadBeforeTransfer, name: "WDRBT")
142                 .WithReservedBits(6, 1)
143                 .WithFlag(7, out localLoopback, name: "LLB")
144                 .WithReservedBits(8, 8)
145                 .WithValueField(16, 4,
146                     writeCallback: (_, val) => ChipSelect(val),
147                     name: "PCS")
148                 .WithReservedBits(20, 4)
149                 .WithTag("DLYBCS", 24, 8)
150                 .WithWriteCallback((_, __) => SetDmaAccessWidth())
151             ;
152 
153             Registers.ReceiveData.Define(this)
154                 .WithValueField(0, 16, out receiveBuffer, FieldMode.Read, name: "RD")
155                 .WithValueField(16, 4, FieldMode.Read,
156                     valueProviderCallback: _ => mode.Value == SPIMode.Master ? rawChipSelect : 0,
157                     name: "PCS")
158                 .WithReservedBits(20, 12)
159                 .WithReadCallback((_, __) =>
160                 {
161                     irqManager.ClearInterrupt(Interrupts.ReceiveDataRegisterFull);
162                     if(isEnabled.Value && mode.Value == SPIMode.Master && waitDataReadBeforeTransfer.Value)
163                     {
164                         TryTransfer();
165                     }
166                 })
167             ;
168 
169             Registers.TransmitData.Define(this)
170                 .WithValueField(0, 16, out transmitData, FieldMode.Write, name: "TD")
171                 .WithValueField(16, 4, out var chipSelect, FieldMode.Write, name: "PCS")
172                 .WithReservedBits(20, 4)
173                 .WithTaggedFlag("LASTXFER", 24)
174                 .WithReservedBits(25, 7)
175                 .WithWriteCallback((_, __) =>
176                 {
177                     transmitBuffer.Clear();
178                     if(peripheralSelectMode.Value == PeripheralSelectMode.Variable)
179                     {
180                         ChipSelect(chipSelect.Value);
181                     }
182                     EnqueueTx(transmitData.Value);
183                     irqManager.ClearInterrupt(Interrupts.TransmissionRegistersEmpty);
184                     if(!isEnabled.Value || mode.Value != SPIMode.Master)
185                     {
186                         return;
187                     }
188                     if(!(waitDataReadBeforeTransfer.Value && irqManager.IsSet(Interrupts.ReceiveDataRegisterFull)))
189                     {
190                         TryTransfer();
191                     }
192                 })
193             ;
194 
195             RegistersCollection.AddRegister((long)Registers.Status,
196                 irqManager.GetRawInterruptFlagRegister<DoubleWordRegister>()
197                     .WithReservedBits(11, 5)
198                     .WithFlag(16, out isEnabled, FieldMode.Read, name: "SPIENS")
199                     .WithReservedBits(17, 15)
200                     .WithReadCallback((_, __) =>
201                     {
202                         // These interrupts are cleared on read
203                         irqManager.ClearInterrupt(Interrupts.ModeFaultError);
204                         irqManager.ClearInterrupt(Interrupts.OverrunError);
205                         irqManager.ClearInterrupt(Interrupts.NSSRising);
206                         irqManager.ClearInterrupt(Interrupts.UnderrunError);
207                     })
208             );
209 
210             RegistersCollection.AddRegister((long)Registers.InterruptEnable,
211                 irqManager.GetInterruptEnableSetRegister<DoubleWordRegister>()
212                     .WithReservedBits(11, 21)
213             );
214 
215             RegistersCollection.AddRegister((long)Registers.InterruptDisable,
216                 irqManager.GetInterruptEnableClearRegister<DoubleWordRegister>()
217                     .WithReservedBits(11, 21)
218             );
219 
220             RegistersCollection.AddRegister((long)Registers.InterruptMask,
221                 irqManager.GetInterruptEnableRegister<DoubleWordRegister>()
222                     .WithReservedBits(11, 21)
223             );
224 
225             // Protected by WPEN
226             Registers.ChipSelect0.DefineMany(this, NumberOfChipSelectRegisters, setup: (register, registerIndex) =>
227                 {
228                     register
229                         .WithTaggedFlag("CPOL", 0)
230                         .WithTaggedFlag("NCPHA", 1)
231                         .WithTaggedFlag("CSNAAT", 2)
232                         .WithTaggedFlag("CSAAT", 3)
233                         .WithValueField(4, 4,
234                             writeCallback: (_, val) =>
235                             {
236                                 txLengths[registerIndex] = val;
237                                 SetDmaAccessWidth();
238                             },
239                             valueProviderCallback: _ =>
240                             {
241                                 return txLengths[registerIndex];
242                             },
243                             name: "BITS")
244                         .WithTag("SCBR", 8, 8)
245                         .WithTag("DLYBS", 16, 8)
246                         .WithTag("DLYBCT", 24, 8);
247                 },
248                 stepInBytes: 4
249             );
250 
251             Registers.WriteProtectionMode.Define(this)
252                 .WithFlag(0, out var enableProtection, name: "WPEN")
253                 .WithReservedBits(1, 7)
254                 .WithValueField(8, 24, out var passkey, name: "WPKEY")
255                 .WithWriteCallback((_, val) =>
256                 {
257                     if(passkey.Value != WriteProtectionPasswd)
258                     {
259                         this.WarningLog("Wrong Write Protection Key! WPVS bit remains unchanged.");
260                         return;
261                     }
262                     writeProtection = enableProtection.Value;
263                 })
264             ;
265 
266             Registers.WriteProtectionStatus.Define(this)
267                 .WithFlag(0, out writeProtectionViolationStatus, FieldMode.ReadToClear, name: "WPVS")
268                 .WithReservedBits(1, 7)
269                 .WithValueField(8, 8, out writeProtectionViolationSource, FieldMode.Read, name: "WPVSRC")
270                 .WithReservedBits(16, 16)
271             ;
272         }
273 
HandlePDCInterrupts()274         private void HandlePDCInterrupts()
275         {
276             if(pdc == null)
277             {
278                 return;
279             }
280             irqManager.SetInterrupt(Interrupts.EndOfReceiveBuffer, pdc.EndOfRxBuffer);
281             irqManager.SetInterrupt(Interrupts.EndOfTransmitBuffer, pdc.EndOfTxBuffer);
282             irqManager.SetInterrupt(Interrupts.TransmitBufferEmpty, pdc.TxBufferEmpty);
283             irqManager.SetInterrupt(Interrupts.ReceiveBufferFull, pdc.RxBufferFull);
284         }
285 
EnqueueTx(ulong val)286         private void EnqueueTx(ulong val)
287         {
288             var byte0 = val & 0xFF;
289             transmitBuffer.Enqueue((byte)byte0);
290             if(SelectedSlaveByteMask > 0)
291             {
292                 var byte1 = ((val & 0xFF00) >> 8) & SelectedSlaveByteMask;
293                 transmitBuffer.Enqueue((byte)byte1);
294             }
295         }
296 
TryTransfer()297         private void TryTransfer()
298         {
299             if(transmitBuffer.Count == 0 || !(TryGetByAddress(selectedSlaveAddr, out var slavePeripheral) || localLoopback.Value))
300             {
301                 return;
302             }
303             if(irqManager.IsSet(Interrupts.ReceiveDataRegisterFull))
304             {
305                 irqManager.SetInterrupt(Interrupts.OverrunError);
306             }
307             DmaReadAccessWidth = (TransferType)transmitBuffer.Count;
308 
309             var transmit = localLoopback.Value ? (Func<byte, byte>)(b => b) : slavePeripheral.Transmit;
310 
311             var transmitted = (ulong)0;
312             receiveBuffer.Value = 0;
313 
314             for(var i = 0; i < transmitBuffer.Count; ++i)
315             {
316                 var b = transmitBuffer.Dequeue();
317                 transmitted |= (ulong)b << (8 * i);
318                 receiveBuffer.Value |= (ulong)transmit(b) << (8 * i);
319             }
320 
321             if(localLoopback.Value)
322             {
323                 this.NoisyLog("Transmitted 0x{0:X} in loopback", transmitted);
324             }
325             else
326             {
327                 slavePeripheral.NoisyLog("Received 0x{0:X}, transmitted 0x{1:X}", transmitted, receiveBuffer.Value);
328             }
329 
330             irqManager.SetInterrupt(Interrupts.ReceiveDataRegisterFull);
331             irqManager.SetInterrupt(Interrupts.TransmissionRegistersEmpty);
332         }
333 
ChipSelect(ulong val)334         private void ChipSelect(ulong val)
335         {
336             rawChipSelect = val;
337             if(!useDecoder.Value)
338             {
339                 // first zero from the least significant side describes the selected slave
340                 selectedSlaveAddr = Misc.Logarithm2((int)BitHelper.GetLeastSignificantZero(val));
341             }
342             else
343             {
344                 selectedSlaveAddr = (int)val;
345             }
346         }
347 
SetDmaAccessWidth()348         private void SetDmaAccessWidth()
349         {
350             if(peripheralSelectMode.Value == PeripheralSelectMode.Variable)
351             {
352                 DmaWriteAccessWidth = TransferType.DoubleWord;
353             }
354             else
355             {
356                 DmaWriteAccessWidth = txLengths[SelectedSlaveRegisterNumber] > 0 ? TransferType.Word : TransferType.Byte;
357             }
358         }
359 
SWReset()360         private void SWReset()
361         {
362             // Software reset does not affect the PDC
363             isEnabled.Value = false;
364             useDecoder.Value = false;
365             waitDataReadBeforeTransfer.Value = false;
366             writeProtection = false;
367             localLoopback.Value = false;
368             selectedSlaveAddr = 0;
369             rawChipSelect = 0;
370             transmitBuffer.Clear();
371             irqManager.SetInterrupt(Interrupts.TransmissionRegistersEmpty);
372             mode.Value = SPIMode.Slave;
373             peripheralSelectMode.Value = PeripheralSelectMode.Fixed;
374             DmaWriteAccessWidth = TransferType.Byte;
375             DmaReadAccessWidth = TransferType.Byte;
376             Array.Clear(txLengths, 0, txLengths.Length);
377         }
378 
379         private int SelectedSlaveRegisterNumber => useDecoder.Value ? (selectedSlaveAddr / 4) : selectedSlaveAddr;
380 
381         private ulong SelectedSlaveByteMask => (ulong)((1 << (int)txLengths[SelectedSlaveRegisterNumber]) - 1);
382 
383         private bool writeProtection;
384         private int selectedSlaveAddr;
385         private ulong rawChipSelect;
386         private ulong[] txLengths = {0, 0, 0, 0};
387         private Queue<byte> transmitBuffer;
388         private IValueRegisterField receiveBuffer;
389         private IValueRegisterField transmitData;
390         private IValueRegisterField writeProtectionViolationSource;
391         private IFlagRegisterField writeProtectionViolationStatus;
392         private IFlagRegisterField isEnabled;
393         private IFlagRegisterField useDecoder;
394         private IFlagRegisterField waitDataReadBeforeTransfer;
395         private IFlagRegisterField localLoopback;
396         private IEnumRegisterField<SPIMode> mode;
397         private IEnumRegisterField<PeripheralSelectMode> peripheralSelectMode;
398         private readonly InterruptManager<Interrupts> irqManager;
399         private readonly SAM_PDC pdc;
400         private readonly List<long> writeProtected = new List<long>
401         {
402             (long)Registers.Mode,
403             (long)Registers.ChipSelect0,
404             (long)Registers.ChipSelect1,
405             (long)Registers.ChipSelect2,
406             (long)Registers.ChipSelect3,
407         };
408         private const int NumberOfChipSelectRegisters = 4;
409         private const int WriteProtectionPasswd = 0x535049; // ASCII: "SPI"
410 
411         public enum Registers
412         {
413             Control                 = 0x00,
414             Mode                    = 0x04,
415             ReceiveData             = 0x08,
416             TransmitData            = 0x0C,
417             Status                  = 0x10,
418             InterruptEnable         = 0x14,
419             InterruptDisable        = 0x18,
420             InterruptMask           = 0x1C,
421             ChipSelect0             = 0x30,
422             ChipSelect1             = 0x34,
423             ChipSelect2             = 0x38,
424             ChipSelect3             = 0x3C,
425             WriteProtectionMode     = 0xE4,
426             WriteProtectionStatus   = 0xE8,
427             // PDC
428             PdcReceivePointer       = 0x100,
429             PdcReceiveCounter       = 0x104,
430             PdcTransmitPointer      = 0x108,
431             PdcTransmitCounter      = 0x10C,
432             PdcReceiveNextPointer   = 0x110,
433             PdcReceiveNextCounter   = 0x114,
434             PdcTransmitNextPointer  = 0x118,
435             PdcTransmitNextCounter  = 0x11C,
436             PdcTransferControl      = 0x120,
437             PdcTransferStatus       = 0x124,
438         }
439 
440         private enum SPIMode : ulong
441         {
442             Slave = 0x0,
443             Master = 0x1,
444         }
445 
446         private enum PeripheralSelectMode : ulong
447         {
448             Fixed = 0x0,
449             Variable = 0x1,
450         }
451 
452         private enum Interrupts
453         {
454             ReceiveDataRegisterFull = 0,
455             TransmitDataRegisterEmpty = 1,
456             ModeFaultError = 2,
457             OverrunError = 3,
458             EndOfReceiveBuffer = 4,
459             EndOfTransmitBuffer = 5,
460             ReceiveBufferFull = 6,
461             TransmitBufferEmpty = 7,
462             NSSRising = 8,
463             TransmissionRegistersEmpty = 9,
464             UnderrunError = 10,
465         }
466     }
467 }
468 
469