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.Collections.Generic;
8 using Antmicro.Renode.Core;
9 using Antmicro.Renode.Core.Structure;
10 using Antmicro.Renode.Core.Structure.Registers;
11 using Antmicro.Renode.Exceptions;
12 using Antmicro.Renode.Logging;
13 using Antmicro.Renode.Peripherals.Bus;
14 using Antmicro.Renode.Utilities;
15 
16 namespace Antmicro.Renode.Peripherals.SPI
17 {
18     public class MPFS_SPI : NullRegistrationPointPeripheralContainer<ISPIPeripheral>, IDoubleWordPeripheral, IKnownSize, ISPIPeripheral
19     {
MPFS_SPI(IMachine machine)20         public MPFS_SPI(IMachine machine) : base(machine)
21         {
22             locker = new object();
23             receiveBuffer = new Queue<byte>();
24             transmitBuffer = new Queue<byte>();
25             IRQ = new GPIO();
26 
27             controlRegister = new DoubleWordRegister(this, 0x80000102)
28                     .WithFlag(0, out coreEnabled,
29                         changeCallback: (_, val) =>
30                         {
31                             if(!val)
32                             {
33                                 frameCounterLimit.Value = 0;
34                             }
35                         }, name: "ENABLE")
36                     .WithFlag(1, out master, name: "MASTER")
37                     .WithTag("MODE", 2, 2)
38                     .WithFlag(4, out enableIrqOnReceive, name: "INTRXDATA")
39                     .WithFlag(5, out enableIrqOnTransmit, name: "INTTXDATA")
40                     .WithFlag(6, out enableIrqOnOverflow, name: "INTRCVOVFLOW")
41                     .WithFlag(7, out enableIrqOnUnderrun, name: "INTTXTURUN")
42                     .WithValueField(8, 16, out frameCounterLimit,
43                         writeCallback: (_, val) =>
44                         {
45                             framesReceived = 0;
46                             framesTransmitted = 0;
47                         }, name: "FRAMECNT")
48                     .WithTag("SPO", 24, 1)
49                     .WithTag("SPH", 25, 1)
50                     .WithTag("SPS", 26, 1)
51                     .WithTag("FRAMEURUN", 27, 1)
52                     .WithTag("CLKMODE", 28, 1)
53                     .WithFlag(29,
54                         changeCallback: (_, val) =>
55                         {
56                             if(val)
57                             {
58                                 if(frameSize.Value <= 8)
59                                 {
60                                     fifoSize = 32;
61                                 }
62                                 else if(frameSize.Value >= 9 && frameSize.Value <= 16)
63                                 {
64                                     fifoSize = 16;
65                                 }
66                                 else if(frameSize.Value >= 17)
67                                 {
68                                     fifoSize = 8;
69                                 }
70                             }
71                             else
72                             {
73                                 fifoSize = 4;
74                             }
75                         }, name: "BIGFIFO")
76                     .WithTag("OENOFF", 30, 1)
77                     .WithFlag(31,
78                         writeCallback: (_, val) =>
79                         {
80                             if(val)
81                             {
82                                 Reset();
83                             }
84                         }, name: "RESET");
85 
86             var registersMap = new Dictionary<long, DoubleWordRegister>
87             {
88                 {(long)Registers.Control, controlRegister},
89 
90                 {(long)Registers.FrameSize, new DoubleWordRegister(this, 0x4)
91                     .WithValueField(0, 6, out frameSize, name: "FRAMESIZE")
92                 },
93 
94                 {(long)Registers.Status, new DoubleWordRegister(this, 0x2440)
95                     .WithFlag(0, out dataSent, FieldMode.Read, name: "TXDATASENT")
96                     .WithFlag(1, out dataReceived, FieldMode.Read, name: "RXDATARCVD")
97                     .WithFlag(2, FieldMode.Read, valueProviderCallback: (_) => receiveOverflow.Value, name: "RXOVERFLOW")
98                     .WithFlag(3, FieldMode.Read, valueProviderCallback: (_) => transmitUnderrun.Value, name: "TXUNDERRUN")
99                     .WithFlag(4, FieldMode.Read, valueProviderCallback: (_) => receiveBuffer.Count == fifoSize, name: "RXFIFOFULL")
100                     .WithFlag(5, FieldMode.Read, valueProviderCallback: (_) => (receiveBuffer.Count + 1) == fifoSize, name: "RXFIFOFULLNEXT")
101                     .WithFlag(6, FieldMode.Read, valueProviderCallback: (_) => receiveBuffer.Count == 0, name: "RXFIFOEMPTY")
102                     .WithFlag(7, FieldMode.Read, valueProviderCallback: (_) => receiveBuffer.Count == 1, name: "RXFIFOEMPTYNEXT")
103                     .WithFlag(8, FieldMode.Read, valueProviderCallback: (_) => transmitBuffer.Count == fifoSize, name: "TXFIFOFULL")
104                     .WithFlag(9, FieldMode.Read, valueProviderCallback: (_) => (transmitBuffer.Count + 1) == fifoSize, name: "TXFIFOFULLNEXT")
105                     .WithFlag(10, FieldMode.Read, valueProviderCallback: (_) => transmitBuffer.Count == 0, name: "TXFIFOEMPTY")
106                     .WithFlag(11, FieldMode.Read, valueProviderCallback: (_) => transmitBuffer.Count == 1, name: "TXFIFOEMPTYNEXT")
107                     .WithTag("FRAMESTART", 12, 1)
108                     .WithTag("SSEL", 13, 1)
109                     .WithTag("ACTIVE", 14, 1)
110                 },
111 
112                 {(long)Registers.InterruptClear, new DoubleWordRegister(this)
113                     .WithFlag(0, FieldMode.WriteOneToClear,
114                         writeCallback: (_, val) =>
115                         {
116                             if(val)
117                             {
118                                 transmitDone.Value = false;
119                             }
120                         }, name: "TXDONE")
121                     .WithFlag(1, FieldMode.WriteOneToClear,
122                         writeCallback: (_, val) =>
123                         {
124                             if(val)
125                             {
126                                 receiveDone.Value = false;
127                             }
128                         }, name: "RXDONE")
129                     .WithFlag(2, FieldMode.WriteOneToClear,
130                         writeCallback: (_, val) =>
131                         {
132                             if(val)
133                             {
134                                 receiveOverflow.Value = false;
135                             }
136                         }, name: "RXOVERFLOW")
137                     .WithFlag(3, FieldMode.WriteOneToClear,
138                         writeCallback: (_, val) =>
139                         {
140                             if(val)
141                             {
142                                 transmitUnderrun.Value = false;
143                             }
144                         }, name: "TXUNDERRUN")
145                     .WithFlag(4, FieldMode.WriteOneToClear,
146                         writeCallback: (_, val) =>
147                         {
148                             if(val)
149                             {
150                                 fullCommandReceived.Value = false;
151                             }
152                         }, name: "CMDINT")
153                     .WithFlag(5, FieldMode.WriteOneToClear,
154                         writeCallback: (_, val) =>
155                         {
156                             if(val)
157                             {
158                                 slaveSelectGoneInactve.Value = false;
159                             }
160                         }, name: "SSEND")
161                 },
162 
163                 {(long)Registers.ReceiveData, new DoubleWordRegister(this)
164                     .WithValueField(0, 32, FieldMode.Read,
165                         valueProviderCallback: _ =>
166                         {
167                             dataReceived.Value = false;
168                             return receiveBuffer.Count > 0 ? receiveBuffer.Dequeue() : (byte)0x00;
169                         }, name: "RXDATA")
170                 },
171 
172                 {(long)Registers.TransmitData, new DoubleWordRegister(this)
173                     .WithValueField(0, 32, FieldMode.Write,
174                         writeCallback: (_, val) =>
175                         {
176                             if(coreEnabled.Value)
177                             {
178                                 dataSent.Value = false;
179                                 if(master.Value == true)
180                                 {
181                                     this.Log(LogLevel.Noisy, $"Writing 0x{val:X} to slave.");
182                                     //TODO: should probably enqueue data if frame counter is enabled and the frameTransmitted reached the threshold.
183                                     // We do not handle TXFIFO as its handling is not clear from the documentation.
184                                     TryReceive(RegisteredPeripheral.Transmit((byte)val));
185                                     if(framesReceived + framesTransmitted == (int)frameCounterLimit.Value)
186                                     {
187                                         dataSent.Value = true;
188                                         transmitDone.Value = true;
189                                         if(slaveSelect.Value > 0)
190                                         {
191                                             RegisteredPeripheral.FinishTransmission();
192                                         }
193                                     }
194                                 }
195                                 else
196                                 {
197                                     this.Log(LogLevel.Noisy, $"Writing 0x{val:X} to transmit buffer in slave mode.");
198                                     transmitBuffer.Enqueue((byte)val);
199                                 }
200                             }
201                         }, name: "TXDATA")
202                 },
203 
204                 {(long)Registers.ClockRate, new DoubleWordRegister(this)
205                     .WithValueField(0, 8, name: "CLKRATE")
206                 },
207 
208                 {(long)Registers.SlaveSelect, new DoubleWordRegister(this)
209                     .WithValueField(0, 8, out slaveSelect,
210                         writeCallback: (_, val) =>
211                         {
212                             if(val > 0)
213                             {
214                                 slaveSelect.Value = val;
215                                 framesReceived = 0;
216                                 framesTransmitted = 0;
217                             }
218                             else if(val == 0 && slaveSelect.Value > 0)
219                             {
220                                 slaveSelect.Value = 0;
221                                 RegisteredPeripheral.FinishTransmission();
222                             }
223                         },  name: "SSEL")
224                 },
225 
226                 {(long)Registers.InterruptMasked, new DoubleWordRegister(this)
227                     .WithValueField(0, 6, valueProviderCallback: _ => CalculateMaskedInterruptValue())
228                 },
229 
230                 {(long)Registers.InterruptRaw, new DoubleWordRegister(this)
231                     .WithFlag(0, out transmitDone, name: "TXDONE")
232                     .WithFlag(1, out receiveDone, name: "RXDONE")
233                     .WithFlag(2, out receiveOverflow, name: "RXOVERFLOW")
234                     .WithFlag(3, out transmitUnderrun, name: "TXUNDERRUN")
235                     .WithFlag(4, out fullCommandReceived, name: "CMDINT")
236                     .WithFlag(5, out slaveSelectGoneInactve, name: "SSEND")
237                 },
238 
239                 {(long)Registers.ControlBitsForEnhancedModes, new DoubleWordRegister(this)
240                     .WithTag("AUTOSTATUS", 0, 1)
241                     .WithTag("AUTOPOLL", 1, 1)
242                     .WithFlag(2, out disableFrameCount, name: "DISFRMCNT")
243                     // bit 3 reserved
244                     .WithFlag(4, out enableIrqOnCmd, name: "INTEN_CMD")
245                     .WithFlag(5, out enableIrqOnSsend, name: "INTEN_SSEND")
246                 },
247 
248                 {(long)Registers.CommandRegister, new DoubleWordRegister(this)
249                     .WithFlag(0, FieldMode.Read,
250                         writeCallback: (_, val) =>
251                         {
252                             if(val)
253                             {
254                                 while(transmitBuffer.Count < fifoSize)
255                                 {
256                                     transmitBuffer.Enqueue(0);
257                                 }
258                             }
259                         }, name: "AUTOFILL")
260                     .WithTag("AUTOEMPTY", 1, 1)
261                     .WithFlag(2, FieldMode.Read,
262                         writeCallback: (_, val) =>
263                         {
264                             if(val)
265                             {
266                                 receiveBuffer.Clear();
267                             }
268                         }, name: "RXFIFORST")
269                     .WithFlag(3, FieldMode.Read,
270                         writeCallback: (_, val) =>
271                         {
272                             if(val)
273                             {
274                                 transmitBuffer.Clear();
275                             }
276                         }, name: "TXFIFORST")
277                     .WithFlag(4, FieldMode.Read,
278                         writeCallback: (_, val) =>
279                         {
280                             if(val)
281                             {
282                                 frameCounterLimit.Value = 0;
283                                 framesTransmitted = 0;
284                                 framesReceived = 0;
285                             }
286                         }, name: "CLRFRAMECNT")
287                     .WithTag("AUTOSTALL", 5, 1)
288                     .WithTag("TXNOW", 6, 1)
289                 },
290 
291                 {(long)Registers.CommandSize, new DoubleWordRegister(this)
292                     .WithValueField(0, 6, out commandSize, name: "CMDSIZE")
293                 },
294 
295                 {(long)Registers.SlaveHardwareStatus, new DoubleWordRegister(this)
296                     .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: (_) => CalculateSlaveHardwareStatus(), name: "HWSTATUS")
297                 },
298 
299                 {(long)Registers.Status8, new DoubleWordRegister(this)
300                     .WithTag("FIRSTFRAME", 0, 1)
301                     .WithFlag(1, FieldMode.Read, valueProviderCallback: (_) => dataSent.Value && dataReceived.Value, name: "DONE")
302                     .WithFlag(2, FieldMode.Read, valueProviderCallback: (_) => receiveBuffer.Count == 0, name: "RXEMPTY")
303                     .WithFlag(3, FieldMode.Read, valueProviderCallback: (_) => transmitBuffer.Count == fifoSize, name: "TXFULL")
304                     .WithFlag(4, FieldMode.Read, valueProviderCallback: (_) => receiveOverflow.Value, name: "RXOVFLOW")
305                     .WithFlag(5, FieldMode.Read, valueProviderCallback: (_) => transmitUnderrun.Value, name: "TXUNDERRUN")
306                     .WithFlag(6, FieldMode.Read, valueProviderCallback: (_) => slaveSelect.Value > 0, name: "SSEL")
307                     .WithTag("ACTIVE", 7, 1)
308                 },
309 
310                 {(long)Registers.AliasedControlRegister0, new DoubleWordRegister(this)
311                     .WithValueField(0, 8,
312                         writeCallback: (_, newVal) => controlRegister.Write(0, (uint)BitHelper.SetMaskedValue(controlRegister.Value, newVal, 0, 8)),
313                         valueProviderCallback: (_) => BitHelper.GetMaskedValue(controlRegister.Value, 0, 8), name: "CTRL0")
314                 },
315 
316                 {(long)Registers.AliasedControlRegister1, new DoubleWordRegister(this)
317                     .WithValueField(0, 8,
318                         writeCallback: (_, newVal) => controlRegister.Write(0, (uint)BitHelper.SetMaskedValue(controlRegister.Value, newVal, 8, 8)),
319                         valueProviderCallback: (_) => BitHelper.GetMaskedValue(controlRegister.Value, 8, 8), name: "CTRL1")
320                 },
321 
322                 {(long)Registers.AliasedControlRegister2, new DoubleWordRegister(this)
323                     .WithValueField(0, 8,
324                         writeCallback: (_, newVal) => controlRegister.Write(0, (uint)BitHelper.SetMaskedValue(controlRegister.Value, newVal, 16, 8)),
325                         valueProviderCallback: (_) => BitHelper.GetMaskedValue(controlRegister.Value, 16, 8), name: "CTRL2")
326                 },
327 
328                 {(long)Registers.AliasedControlRegister3, new DoubleWordRegister(this)
329                     .WithValueField(0, 8,
330                         writeCallback: (_, newVal) => controlRegister.Write(0, (uint)BitHelper.SetMaskedValue(controlRegister.Value, newVal, 24, 8)),
331                         valueProviderCallback: (_) => BitHelper.GetMaskedValue(controlRegister.Value, 24, 8), name: "CTRL3")
332                 }
333             };
334 
335             registers = new DoubleWordRegisterCollection(this, registersMap);
336         }
337 
Reset()338         public override void Reset()
339         {
340             fifoSize = 0;
341             framesReceived = 0;
342             framesTransmitted = 0;
343 
344             receiveBuffer.Clear();
345             transmitBuffer.Clear();
346 
347             registers.Reset();
348             RefreshInterrupt();
349         }
350 
ReadDoubleWord(long offset)351         public uint ReadDoubleWord(long offset)
352         {
353             var result = 0u;
354             lock(locker)
355             {
356                 result = registers.Read(offset);
357             }
358             RefreshInterrupt();
359             return result;
360         }
361 
WriteDoubleWord(long offset, uint value)362         public void WriteDoubleWord(long offset, uint value)
363         {
364             lock(locker)
365             {
366                 registers.Write(offset, value);
367             }
368             RefreshInterrupt();
369         }
370 
Transmit(byte data)371         public byte Transmit(byte data)
372         {
373             var returnValue = (byte)0x00;
374             lock(locker)
375             {
376                 if(master.Value)
377                 {
378                     this.Log(LogLevel.Warning, "Cannot receive data when in master mode.");
379                     return returnValue;
380                 }
381                 if(!coreEnabled.Value)
382                 {
383                     this.Log(LogLevel.Warning, "Cannot receive due to inactive core.");
384                     return returnValue;
385                 }
386                 TryReceive(data);
387                 if(framesReceived == (int)commandSize.Value)
388                 {
389                     fullCommandReceived.Value = true;
390                     returnValue = CalculateSlaveHardwareStatus();
391                 }
392                 if(transmitBuffer.Count > 0)
393                 {
394                     framesTransmitted++;
395                     returnValue = transmitBuffer.Dequeue();
396                 }
397                 else
398                 {
399                     transmitUnderrun.Value = true;
400                 }
401             }
402             RefreshInterrupt();
403             return returnValue;
404         }
405 
FinishTransmission()406         public void FinishTransmission()
407         {
408             framesReceived = 0;
409             framesTransmitted = 0;
410             slaveSelectGoneInactve.Value = true;
411             RefreshInterrupt();
412         }
413 
414         public long Size => 0x1000;
415 
416         public GPIO IRQ { get; }
417 
TryReceive(byte data)418         private void TryReceive(byte data)
419         {
420             if(receiveBuffer.Count < (fifoSize * (int)frameSize.Value))
421             {
422                 framesReceived++;
423                 receiveBuffer.Enqueue(data);
424                 // Docs are not clear if this should be done in slave or master mode.
425                 if(!disableFrameCount.Value && receiveBuffer.Count % (int)frameSize.Value == 0)
426                 {
427                     if(framesTransmitted + framesReceived == (int)frameCounterLimit.Value)
428                     {
429                         dataReceived.Value = true;
430                         receiveDone.Value = true;
431                     }
432                 }
433             }
434             else
435             {
436                 receiveOverflow.Value = true;
437             }
438         }
439 
CalculateMaskedInterruptValue()440         private uint CalculateMaskedInterruptValue()
441         {
442             var result = new bool[6]
443             {
444                 enableIrqOnReceive.Value && receiveDone.Value,
445                 enableIrqOnTransmit.Value && transmitDone.Value,
446                 enableIrqOnOverflow.Value && receiveOverflow.Value,
447                 enableIrqOnUnderrun.Value && transmitUnderrun.Value,
448                 fullCommandReceived.Value && enableIrqOnCmd.Value,
449                 slaveSelectGoneInactve.Value && enableIrqOnSsend.Value
450             };
451             return BitHelper.GetValueFromBitsArray(result);
452         }
453 
CalculateSlaveHardwareStatus()454         private byte CalculateSlaveHardwareStatus()
455         {
456             var result = new bool[2]
457             {
458                 receiveBuffer.Count > 0,
459                 transmitBuffer.Count == 0
460             };
461             return (byte)BitHelper.GetValueFromBitsArray(result);
462         }
463 
RefreshInterrupt()464         private void RefreshInterrupt()
465         {
466             IRQ.Set(CalculateMaskedInterruptValue() != 0);
467         }
468 
469         private readonly DoubleWordRegisterCollection registers;
470         private readonly DoubleWordRegister controlRegister;
471         private readonly Queue<byte> receiveBuffer;
472         private readonly Queue<byte> transmitBuffer;
473         private IValueRegisterField frameCounterLimit;
474         private IValueRegisterField slaveSelect;
475         private IFlagRegisterField coreEnabled;
476         private IFlagRegisterField master;
477         private IFlagRegisterField enableIrqOnReceive;
478         private IFlagRegisterField enableIrqOnTransmit;
479         private IFlagRegisterField enableIrqOnOverflow;
480         private IFlagRegisterField enableIrqOnUnderrun;
481         private IValueRegisterField frameSize;
482         private IFlagRegisterField dataSent;
483         private IFlagRegisterField dataReceived;
484         private IFlagRegisterField receiveOverflow;
485         private IFlagRegisterField transmitUnderrun;
486         private IFlagRegisterField fullCommandReceived;
487         private IFlagRegisterField slaveSelectGoneInactve;
488         private IFlagRegisterField transmitDone;
489         private IFlagRegisterField receiveDone;
490         private IFlagRegisterField disableFrameCount;
491         private IFlagRegisterField enableIrqOnCmd;
492         private IValueRegisterField commandSize;
493         private IFlagRegisterField enableIrqOnSsend;
494         private int fifoSize;
495         private int framesReceived;
496         private int framesTransmitted;
497         private object locker;
498 
499         private enum Registers : long
500         {
501             Control = 0x00,
502             FrameSize = 0x04,
503             Status = 0x08,
504             InterruptClear = 0x0c,
505             ReceiveData = 0x10,
506             TransmitData = 0x14,
507             ClockRate = 0x18,
508             SlaveSelect = 0x1c,
509             InterruptMasked = 0x20,
510             InterruptRaw = 0x24,
511             ControlBitsForEnhancedModes = 0x28,
512             CommandRegister = 0x2c,
513             PacketSize = 0x30,
514             CommandSize = 0x34,
515             SlaveHardwareStatus = 0x38,
516             Status8 = 0x3c,
517             AliasedControlRegister0 = 0x40,
518             AliasedControlRegister1 = 0x44,
519             AliasedControlRegister2 = 0x48,
520             AliasedControlRegister3 = 0x4c
521         }
522     }
523 }
524