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 
8 using System.Linq;
9 using System.Collections.Generic;
10 using Antmicro.Renode.Core;
11 using Antmicro.Renode.Core.Structure;
12 using Antmicro.Renode.Core.Structure.Registers;
13 using Antmicro.Renode.Logging;
14 using Antmicro.Renode.Peripherals.Bus;
15 
16 namespace Antmicro.Renode.Peripherals.SPI
17 {
18     public class NRF52840_SPI : NullRegistrationPointPeripheralContainer<ISPIPeripheral>, IDoubleWordPeripheral, IProvidesRegisterCollection<DoubleWordRegisterCollection>, IKnownSize
19     {
NRF52840_SPI(IMachine machine, bool easyDMA = false)20         public NRF52840_SPI(IMachine machine, bool easyDMA = false) : base(machine)
21         {
22             this.machine = machine;
23             sysbus = machine.GetSystemBus(this);
24             this.easyDMA = easyDMA;
25 
26             IRQ = new GPIO();
27 
28             receiveFifo = new Queue<byte>();
29             RegistersCollection = new DoubleWordRegisterCollection(this);
30             DefineRegisters();
31             Reset();
32         }
33 
Reset()34         public override void Reset()
35         {
36             receiveFifo.Clear();
37             enabled = false;
38             RegistersCollection.Reset();
39             UpdateInterrupts();
40         }
41 
ReadDoubleWord(long offset)42         public uint ReadDoubleWord(long offset)
43         {
44             return RegistersCollection.Read(offset);
45         }
46 
WriteDoubleWord(long offset, uint value)47         public void WriteDoubleWord(long offset, uint value)
48         {
49             RegistersCollection.Write(offset, value);
50         }
51 
52         public GPIO IRQ { get; }
53 
54         public DoubleWordRegisterCollection RegistersCollection { get; }
55 
56         public long Size => 0x1000;
57 
DefineRegisters()58         private void DefineRegisters()
59         {
60             Registers.PendingInterrupt.Define(this)
61                 .WithFlag(0, out readyPending, name: "EVENTS_READY")
62                 .WithReservedBits(1, 31)
63                 .WithWriteCallback((_, __) => UpdateInterrupts())
64             ;
65 
66             if(easyDMA)
67             {
68                 Registers.EnableInterrupt.Define(this)
69                     .WithReservedBits(0, 1)
70                     .WithTaggedFlag("STOPPED", 1)
71                     .WithReservedBits(2, 2)
72                     .WithFlag(4, out endRxEnabled, FieldMode.Read | FieldMode.Set, name: "ENDRX")
73                     .WithReservedBits(5, 1)
74                     .WithFlag(6, out endEnabled, FieldMode.Read | FieldMode.Set, name: "END")
75                     .WithReservedBits(7, 1)
76                     .WithFlag(8, out endTxEnabled, FieldMode.Read | FieldMode.Set, name: "ENDTX")
77                     .WithReservedBits(9, 10)
78                     .WithFlag(19, out startedEnabled, FieldMode.Read | FieldMode.Set, name: "STARTED")
79                     .WithReservedBits(20, 12)
80                     .WithWriteCallback((_, __) => UpdateInterrupts())
81                 ;
82 
83                 Registers.DisableInterrupt.Define(this)
84                     .WithReservedBits(0, 1)
85                     .WithTaggedFlag("STOPPED", 1)
86                     .WithReservedBits(2, 2)
87                     .WithFlag(4, name: "ENDRX",
88                         valueProviderCallback: _ => endRxEnabled.Value,
89                         writeCallback: (_, val) => { if(val) endRxEnabled.Value = false; })
90                     .WithReservedBits(5, 1)
91                     .WithFlag(6, name: "END",
92                         valueProviderCallback: _ => endEnabled.Value,
93                         writeCallback: (_, val) => { if(val) endEnabled.Value = false; })
94                     .WithReservedBits(7, 1)
95                     .WithFlag(8, name: "ENDTX",
96                         valueProviderCallback: _ => endTxEnabled.Value,
97                         writeCallback: (_, val) => { if(val) endTxEnabled.Value = false; })
98                     .WithReservedBits(9, 10)
99                     .WithFlag(19, name: "STARTED",
100                         valueProviderCallback: _ => startedEnabled.Value,
101                         writeCallback: (_, val) => { if(val) startedEnabled.Value = false; })
102                     .WithReservedBits(20, 12)
103                     .WithWriteCallback((_, __) => UpdateInterrupts())
104                 ;
105 
106                 Registers.TxListType.Define(this)
107                     .WithTaggedFlag("LIST - List type", 0)
108                     .WithReservedBits(1, 31)
109                 ;
110 
111                 Registers.RxListType.Define(this)
112                     .WithTaggedFlag("LIST - List type", 0)
113                     .WithReservedBits(1, 31)
114                 ;
115             }
116             else
117             {
118                 Registers.EnableInterrupt.Define(this)
119                     .WithReservedBits(0, 2)
120                     .WithFlag(2, out readyEnabled, FieldMode.Read | FieldMode.Set, name: "READY")
121                     .WithReservedBits(3, 3)
122                     .WithFlag(6, out endEnabled, FieldMode.Read | FieldMode.Set, name: "END")
123                     .WithReservedBits(7, 25)
124                     .WithWriteCallback((_, __) => UpdateInterrupts())
125                 ;
126 
127                 Registers.DisableInterrupt.Define(this)
128                     .WithReservedBits(0, 2)
129                     .WithFlag(2, name: "READY",
130                         valueProviderCallback: _ => readyEnabled.Value,
131                         writeCallback: (_, val) => { if(val) readyEnabled.Value = false; })
132                     .WithReservedBits(3, 3)
133                     .WithFlag(6, name: "END",
134                         writeCallback: (_, val) => { if(val) endEnabled.Value = false; })
135                     .WithReservedBits(7, 25)
136                     .WithWriteCallback((_, __) => UpdateInterrupts())
137                 ;
138             }
139 
140             Registers.Enable.Define(this)
141                 .WithValueField(0, 4,
142                     valueProviderCallback: _ => enabled ? 1 : 0u,
143                     writeCallback: (_, val) =>
144                     {
145                         switch(val)
146                         {
147                             case 0:
148                                 // disabled
149                                 enabled = false;
150                                 break;
151 
152                             case 1:
153                                 // enabled, standard mode
154                                 if(!easyDMA)
155                                 {
156                                     enabled = true;
157                                 }
158                                 break;
159 
160                             case 7:
161                                 // enabled, easyDMA mode
162                                 if(easyDMA)
163                                 {
164                                     enabled = true;
165                                 }
166                                 break;
167 
168                             default:
169                                 this.Log(LogLevel.Warning, "Unhandled enable value: 0x{0:X}", val);
170                                 return;
171                         }
172 
173                         UpdateInterrupts();
174                     })
175                 .WithReservedBits(4, 28)
176             ;
177 
178             Registers.TransmitBuffer.Define(this)
179                 // the documentation says this field is readable, so we use the automatic
180                 // underlying backing field to return the previously written value
181                 .WithValueField(0, 8, name: "TXD", writeCallback: (_, val) => SendData((byte)val))
182                 .WithReservedBits(8, 24)
183             ;
184 
185             Registers.ReceiveBuffer.Define(this)
186                 .WithValueField(0, 8, FieldMode.Read, name: "RXD",
187                     valueProviderCallback: _ =>
188                     {
189                         if(receiveFifo.Count == 0)
190                         {
191                             this.Log(LogLevel.Warning, "Tried to read from an empty buffer");
192                             return 0;
193                         }
194 
195                         lock(receiveFifo)
196                         {
197                             var result = receiveFifo.Dequeue();
198 
199                             // some new byte moved to the head
200                             // of the queue - let's generate the
201                             // READY event
202                             if(receiveFifo.Count > 0)
203                             {
204                                 readyPending.Value = true;
205                                 UpdateInterrupts();
206                             }
207                             return result;
208                         }
209                     })
210                 .WithReservedBits(8, 24)
211             ;
212 
213             if(easyDMA)
214             {
215                 Registers.TasksStart.Define(this)
216                     .WithFlag(0, FieldMode.Write, name: "TASKS_START", writeCallback: (_, val) =>
217                     {
218                         if(val)
219                         {
220                             machine.LocalTimeSource.ExecuteInNearestSyncedState(__ => ExecuteTransaction());
221                         }
222                     })
223                     .WithReservedBits(1, 31)
224                 ;
225 
226                 Registers.EventsStarted.Define(this)
227                     .WithFlag(0, out startedPending, name: "EVENTS_STARTED")
228                     .WithReservedBits(1, 31)
229                     .WithWriteCallback((_, __) => UpdateInterrupts())
230                 ;
231 
232                 Registers.EventsEnd.Define(this)
233                     .WithFlag(0, out endPending, name: "EVENTS_END")
234                     .WithReservedBits(1, 31)
235                     .WithWriteCallback((_, __) => UpdateInterrupts())
236                 ;
237 
238                 Registers.EventsEndTx.Define(this)
239                     .WithFlag(0, out endTxPending, name: "EVENTS_ENDTX")
240                     .WithReservedBits(1, 31)
241                     .WithWriteCallback((_, __) => UpdateInterrupts())
242                 ;
243 
244                 Registers.EventsEndRx.Define(this)
245                     .WithFlag(0, out endRxPending, name: "EVENTS_ENDRX")
246                     .WithReservedBits(1, 31)
247                     .WithWriteCallback((_, __) => UpdateInterrupts())
248                 ;
249 
250                 Registers.RxDataPointer.Define(this)
251                     .WithValueField(0, 32, out rxDataPointer, name: "PTR")
252                 ;
253 
254                 Registers.RxMaxDataCount.Define(this)
255                     .WithValueField(0, 16, out rxMaxDataCount, name: "MAXCNT")
256                     .WithReservedBits(16, 16)
257                 ;
258 
259                 Registers.RxTransferredDataAmount.Define(this)
260                     .WithValueField(0, 16, out rxTransferredDataAmount, FieldMode.Read, name: "AMOUNT")
261                     .WithReservedBits(16, 16)
262                 ;
263 
264                 Registers.TxDataPointer.Define(this)
265                     .WithValueField(0, 32, out txDataPointer, name: "PTR")
266                 ;
267 
268                 Registers.TxMaxDataCount.Define(this)
269                     .WithValueField(0, 16, out txMaxDataCount, name: "MAXCNT")
270                     .WithReservedBits(16, 16)
271                 ;
272 
273                 Registers.TxTransferredDataAmount.Define(this)
274                     .WithValueField(0, 16, out txTransferredDataAmount, FieldMode.Read, name: "AMOUNT")
275                     .WithReservedBits(16, 16)
276                 ;
277 
278                 Registers.ORC.Define(this)
279                     .WithValueField(0, 8, out orcByte, name: "ORC")
280                     .WithReservedBits(8, 24)
281                 ;
282             }
283         }
284 
ExecuteTransaction()285         private void ExecuteTransaction()
286         {
287             var receivedBytes = new byte[rxMaxDataCount.Value];
288 
289             startedPending.Value = true;
290 
291             if(RegisteredPeripheral == null)
292             {
293                 this.Log(LogLevel.Warning, "Issued a transaction, but no device is connected - returning dummy bytes");
294 
295                 // in case there is no target device `receivedBytes` contain just zeros in our simulation
296                 // (on a real HW that could be any nondeterministic value)
297             }
298             else
299             {
300                 this.Log(LogLevel.Debug, "Starting SPI transaction using easyDMA interface");
301 
302                 var bytesToSend = sysbus.ReadBytes(txDataPointer.Value, (int)txMaxDataCount.Value);
303                 if(rxMaxDataCount.Value > txMaxDataCount.Value)
304                 {
305                     // fill the rest of bytes to transmit with the ORC byte
306                     bytesToSend = bytesToSend.Concat(Enumerable.Repeat((byte)orcByte.Value, (int)(rxMaxDataCount.Value - txMaxDataCount.Value))).ToArray();
307                 }
308 
309                 var counter = 0;
310                 foreach(var b in bytesToSend)
311                 {
312                     var result = RegisteredPeripheral.Transmit(b);
313                     if(counter < receivedBytes.Length)
314                     {
315                         receivedBytes[counter++] = result;
316                     }
317                     // bytes over rxMaxDataCount are being discarded
318                 }
319             }
320 
321             sysbus.WriteBytes(receivedBytes, rxDataPointer.Value);
322 
323             endTxPending.Value = true;
324             endRxPending.Value = true;
325             endPending.Value = true;
326             UpdateInterrupts();
327         }
328 
SendData(byte b)329         private void SendData(byte b)
330         {
331             if(!enabled)
332             {
333                 this.Log(LogLevel.Warning, "Trying to send data, but the controller is disabled");
334                 return;
335             }
336 
337             if(RegisteredPeripheral == null)
338             {
339                 this.Log(LogLevel.Warning, "No device connected");
340                 return;
341             }
342 
343             if(receiveFifo.Count == ReceiveBufferSize)
344             {
345                 this.Log(LogLevel.Warning, "Buffers full, ignoring data");
346                 return;
347             }
348 
349             // there is no need to queue transmitted bytes - let's send them right away
350             var result = RegisteredPeripheral.Transmit(b);
351             lock(receiveFifo)
352             {
353                 receiveFifo.Enqueue(result);
354 
355                 // the READY event is generated
356                 // only when the head
357                 // of the queue changes
358                 if(receiveFifo.Count == 1)
359                 {
360                     readyPending.Value = true;
361                     UpdateInterrupts();
362                 }
363             }
364         }
365 
366         // RXD is double buffered
367         private const int ReceiveBufferSize = 2;
368 
UpdateInterrupts()369         private void UpdateInterrupts()
370         {
371             var status = false;
372 
373             if(easyDMA)
374             {
375                 status |= startedPending.Value && startedEnabled.Value;
376                 status |= endPending.Value && endEnabled.Value;
377                 status |= endRxPending.Value && endRxEnabled.Value;
378                 status |= endTxPending.Value && endTxEnabled.Value;
379             }
380             else
381             {
382                 status |= readyEnabled.Value && readyPending.Value;
383             }
384             status &= enabled;
385 
386             this.Log(LogLevel.Noisy, "Setting IRQ to {0}", status);
387             IRQ.Set(status);
388         }
389 
390         private IFlagRegisterField startedPending;
391         private IFlagRegisterField startedEnabled;
392 
393         private IFlagRegisterField readyPending;
394         private IFlagRegisterField readyEnabled;
395 
396         private IFlagRegisterField endEnabled;
397         private IFlagRegisterField endPending;
398 
399         private IFlagRegisterField endTxEnabled;
400         private IFlagRegisterField endTxPending;
401 
402         private IFlagRegisterField endRxEnabled;
403         private IFlagRegisterField endRxPending;
404 
405         private IValueRegisterField txDataPointer;
406         private IValueRegisterField rxDataPointer;
407         private IValueRegisterField txMaxDataCount;
408         private IValueRegisterField rxMaxDataCount;
409         private IValueRegisterField txTransferredDataAmount;
410         private IValueRegisterField rxTransferredDataAmount;
411 
412         private IValueRegisterField orcByte;
413 
414         private bool enabled;
415 
416         private readonly Queue<byte> receiveFifo;
417         private readonly IMachine machine;
418         private readonly IBusController sysbus;
419         private readonly bool easyDMA;
420 
421         private enum Registers
422         {
423             TasksStart = 0x10,
424             PendingInterrupt = 0x108,
425             EventsEndRx = 0x110,
426             EventsEnd = 0x118,
427             EventsEndTx = 0x120,
428             EventsStarted = 0x14C,
429             EnableInterrupt = 0x304,
430             DisableInterrupt = 0x308,
431             Enable = 0x500,
432             PinSelectSCK = 0x508,
433             PinSelectMOSI = 0x50C,
434             PinSelectMISO = 0x510,
435             ReceiveBuffer = 0x518,
436             TransmitBuffer = 0x51C,
437             Frequency = 0x524,
438             RxDataPointer = 0x534,
439             RxMaxDataCount = 0x538,
440             RxTransferredDataAmount = 0x53C,
441             RxListType = 0x540,
442             TxDataPointer = 0x544,
443             TxMaxDataCount = 0x548,
444             TxTransferredDataAmount = 0x54C,
445             TxListType = 0x550,
446             Configuration = 0x554,
447             ORC = 0x5C0,
448         }
449     }
450 }
451