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 System.Linq;
10 using Antmicro.Renode.Peripherals.Bus;
11 using Antmicro.Renode.Peripherals.Helpers;
12 using Antmicro.Renode.Core;
13 using Antmicro.Renode.Core.Structure;
14 using Antmicro.Renode.Core.Structure.Registers;
15 using Antmicro.Renode.Logging;
16 using Antmicro.Renode.Utilities;
17 
18 namespace Antmicro.Renode.Peripherals.I2C
19 {
20     [AllowedTranslations(AllowedTranslation.WordToDoubleWord)]
21     public class Cadence_I2C : SimpleContainer<II2CPeripheral>, IDoubleWordPeripheral, IKnownSize
22     {
Cadence_I2C(IMachine machine)23         public Cadence_I2C(IMachine machine) : base(machine)
24         {
25             IRQ = new GPIO();
26             rxFifo = new Queue<byte>();
27             txFifo = new Queue<byte>(FifoCapacity);
28             txTransfer = new Queue<byte>();
29             registers = new DoubleWordRegisterCollection(this, BuildRegisterMap());
30 
31             txFifoOverflow = new CadenceInterruptFlag();
32             rxFifoOverflow = new CadenceInterruptFlag();
33             rxFifoUnderflow = new CadenceInterruptFlag();
34             targetReady = new CadenceInterruptFlag();
35             transferNotAcknowledged = new CadenceInterruptFlag();
36             transferNewData = new CadenceInterruptFlag();
37             transferCompleted = new CadenceInterruptFlag();
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 
50         public long Size => 0x40;
51 
52         public GPIO IRQ { get; }
53 
Reset()54         public override void Reset()
55         {
56             registers.Reset();
57             targetDevice = null;
58             transferState = TransferState.Idle;
59 
60             ClearFifos();
61             txTransfer.Clear();
62 
63             foreach(var flag in GetInterruptFlags())
64             {
65                 flag.Reset();
66             }
67             UpdateInterrupts();
68         }
69 
ClearFifos()70         private void ClearFifos()
71         {
72             rxFifo.Clear();
73             txFifo.Clear();
74             // transferSize relates to the FIFOs counts, so it's also reseted
75             transferSize.Value = 0;
76         }
77 
UpdateInterrupts()78         private void UpdateInterrupts()
79         {
80             var newState = GetInterruptFlags().Any(x => x.InterruptStatus);
81             if(IRQ.IsSet != newState)
82             {
83                 this.Log(LogLevel.Debug, "Setting IRQ to {0}", newState);
84                 IRQ.Set(newState);
85             }
86         }
87 
EnqueueTx(byte data)88         private void EnqueueTx(byte data)
89         {
90             if(transferState != TransferState.Transmitting)
91             {
92                 // Before transmission start data are collected in the txFifo
93                 if(txFifo.Count == FifoCapacity)
94                 {
95                     this.Log(LogLevel.Warning, "Trying to write to a full Tx FIFO.");
96                     txFifoOverflow.SetSticky(true);
97                     return;
98                 }
99                 txFifo.Enqueue(data);
100                 transferSize.Value = (uint)txFifo.Count;
101             }
102             else
103             {
104                 // When transmission is ongoing data are collected in the txTransfer for send to a peripheral at once
105                 txTransfer.Enqueue(data);
106                 transferSize.Value = 0;
107                 transferCompleted.SetSticky(true);
108             }
109         }
110 
DequeueRx()111         private byte DequeueRx()
112         {
113             if(!rxFifo.TryDequeue(out var data))
114             {
115                 this.Log(LogLevel.Warning, "Trying to read from an empty Rx FIFO");
116                 rxFifoUnderflow.SetSticky(true);
117                 return default(byte);
118             }
119 
120             // RX interrupts are triggered at each FIFO reading to mimic a reception byte by byte
121             SetRxSticky();
122             transferSize.Value = (uint)rxFifo.Count;
123             return data;
124         }
125 
126         // TransferTriggered argument is set only by the transferAddress write callback which triggers transfer
TryChangeState(bool transferTriggered)127         private bool TryChangeState(bool transferTriggered)
128         {
129             var oldState = transferState;
130             ChangeState(transferTriggered);
131             if(oldState != transferState)
132             {
133                 OnStateChange(oldState, transferState);
134                 return true;
135             }
136             return false;
137         }
138 
ChangeState(bool transferTriggered)139         private void ChangeState(bool transferTriggered)
140         {
141             if(transferTriggered)
142             {
143                 switch(transferDirection.Value)
144                 {
145                     case TransferDirection.Transmit:
146                         transferState = TransferState.Transmitting;
147                         break;
148                     case TransferDirection.Receive:
149                         transferState = TransferState.Receiving;
150                         break;
151                     default:
152                         throw new Exception($"Unsupported TransferSize enum member {transferDirection.Value}");
153                 }
154             }
155             else if(targetDevice == null || !transferHold.Value)
156             {
157                 // There is second call of the TryChangeState in the transferAddress write callback to immediately return to the idle state in case of target non-appearence
158                 // Also transferHold flag clear causes return to the idle state
159                 transferState = TransferState.Idle;
160             }
161         }
162 
OnStateChange(TransferState oldState, TransferState state)163         private void OnStateChange(TransferState oldState, TransferState state)
164         {
165             if(oldState == TransferState.Idle)
166             {
167                 if(targetDevice == null)
168                 {
169                     this.Log(LogLevel.Warning, "Can't find an I2C peripheral at address 0x{0:X}.", TransferAddress);
170                     transferNotAcknowledged.SetSticky(true);
171                 }
172                 else if(targetMonitorEnabled.Value)
173                 {
174                     targetReady.SetSticky(true);
175                 }
176             }
177 
178             if(state == TransferState.Transmitting)
179             {
180                 if(targetDevice != null)
181                 {
182                     txTransfer.EnqueueRange(txFifo.DequeueAll());
183                     transferCompleted.SetSticky(true);
184                 }
185                 else
186                 {
187                     // Even in the case of a target non-appearance one byte is read from the Tx FIFO
188                     // It's assumed that hardware sends at least one byte to receive NACK
189                     txFifo.TryDequeue(out var result);
190                 }
191                 transferSize.Value = (uint)txFifo.Count;
192             }
193 
194             if(oldState == TransferState.Transmitting)
195             {
196                 if(targetDevice != null)
197                 {
198                     // Flushing at once all data collected during transmission to the target device
199                     targetDevice.Write(txTransfer.DequeueAll());
200                 }
201             }
202 
203             if(state == TransferState.Receiving)
204             {
205                 if(rxFifo.Count > 0)
206                 {
207                     this.Log(LogLevel.Warning, "Starting new read operation when Rx FIFO isn't empty. TransferSize value may be corrupted.");
208                     // Receiving data without a FIFO clear isn't recommended and isn't covered in detail by the datasheet.
209                     // This peripheral reads all data at once from an I2C peripheral, so we need to drop the data in that case.
210                     // Keeping some data mimics the FIFO full of data from the previous transaction.
211                     var readData = rxFifo.DequeueRange(FifoCapacity);
212                     rxFifo.Clear();
213                     rxFifo.EnqueueRange(readData);
214                 }
215 
216                 if(targetDevice != null)
217                 {
218                     // All data are read from the target device at once
219                     var size = (int)transferSize.Value;
220                     var data = targetDevice.Read(size);
221                     if(data.Length < size)
222                     {
223                         this.Log(LogLevel.Warning, "The I2C peripheral returns less bytes ({0}) than expected ({1}).", data.Length, size);
224                     }
225                     else if(data.Length > size)
226                     {
227                         this.Log(LogLevel.Warning, "The I2C peripheral returns more bytes ({0}) than expected ({1}).", data.Length, size);
228                     }
229 
230                     if(rxFifo.Count + size <= MaxTransferSize)
231                     {
232                         // When data from the previous transaction and transfer data fit into FIFO, we may just enqueue it.
233                         rxFifo.EnqueueRange(data, size);
234                         // In case of returning too little data from the I2C peripheral, some dummy data are enqueued.
235                         for(var i = data.Length; i < size; i++)
236                         {
237                             rxFifo.Enqueue(default(byte));
238                         }
239                     }
240                     else
241                     {
242                         this.Log(LogLevel.Error, "Due to not empty Rx FIFO some data read from the I2C target can't be queued.");
243                         rxFifo.EnqueueRange(data, size - rxFifo.Count);
244                     }
245                 }
246 
247                 SetRxSticky();
248             }
249 
250             if(state == TransferState.Idle)
251             {
252                 if(targetDevice != null)
253                 {
254                     targetDevice.FinishTransmission();
255                 }
256             }
257         }
258 
SetRxSticky()259         private void SetRxSticky()
260         {
261             if(rxFifo.Count == 0)
262             {
263                 return;
264             }
265 
266             transferNewData.SetSticky(true);
267             // The I2C interface implementation in Renode operates on byte collections - it sends all the data to the device in one go and receives one response.
268             // As a result it can receive more data that could fit in the controller's FIFO (`rxFifo.Count > FifoCapacity`). We need to handle it correctly in the model.
269             if(rxFifo.Count <= FifoCapacity)
270             {
271                 transferCompleted.SetSticky(true);
272             }
273         }
274 
BuildRegisterMap()275         private Dictionary<long, DoubleWordRegister> BuildRegisterMap()
276         {
277             return new Dictionary<long, DoubleWordRegister>
278             {
279                 {(long)Registers.Control, new DoubleWordRegister(this)
280                     .WithReservedBits(16, 16)
281                     .WithTag("divisorA", 14, 2)
282                     .WithTag("divisorB", 8, 6)
283                     .WithReservedBits(7, 1)
284                     .WithFlag(6, FieldMode.Read | FieldMode.WriteOneToClear, name: "clearFifo",
285                         writeCallback: (_, val) => { if(val) ClearFifos(); }
286                     )
287                     .WithFlag(5, out targetMonitorEnabled, name: "targetMonitorEnabled")
288                     .WithFlag(4, out transferHold, name: "transferHold",
289                         writeCallback: (_, __) => TryChangeState(transferTriggered: false))
290                     .WithTaggedFlag("transferAcknowledge", 3)
291                     .WithEnumField(2, 1, out addressingMode, name: "addressingMode")
292                     .WithTaggedFlag("interfaceMode", 1)
293                     .WithEnumField(0, 1, out transferDirection, name: "transferDirection",
294                         changeCallback: (prevVal, val) =>
295                         {
296                             if(transferState != TransferState.Idle && transferSize.Value > 0)
297                             {
298                                 this.Log(LogLevel.Warning, "Changing transfer direction when transfer isn't completed.");
299                             }
300                         }
301                     )
302                     .WithWriteCallback((_, __) => UpdateInterrupts())
303                 },
304                 {(long)Registers.Status, new DoubleWordRegister(this)
305                     .WithReservedBits(9, 2)
306                     .WithFlag(8, FieldMode.Read, name: "busActive",
307                         valueProviderCallback: (_) => transferState != TransferState.Idle
308                     )
309                     .WithFlag(7, FieldMode.Read, name: "rxFifoOverflow",
310                         // It's always false, there is no way to overflow the Rx FIFO
311                         valueProviderCallback: (_) => false
312                     )
313                     .WithFlag(6, FieldMode.Read, name: "txFifoNotEmpty",
314                         valueProviderCallback: (_) => txFifo.Count > 0
315                     )
316                     .WithFlag(5, FieldMode.Read, name: "rxFifoNotEmpty",
317                         valueProviderCallback: (_) => rxFifo.Count > 0
318                     )
319                     .WithReservedBits(4, 1)
320                     .WithTaggedFlag("targetModeTransferDirection", 3)
321                     .WithReservedBits(0, 3)
322                 },
323                 {(long)Registers.TransferAddress, new DoubleWordRegister(this)
324                     .WithReservedBits(10, 22)
325                     .WithValueField(0, 10, out transferAddressReg, name: "transferAddress",
326                         writeCallback: (prevVal, val) =>
327                         {
328                             if(transferState != TransferState.Idle && prevVal != val)
329                             {
330                                 this.Log(LogLevel.Error, "Changing the transfer address during transmission.");
331                             }
332 
333                             TryGetByAddress(TransferAddress, out targetDevice);
334                             if(TryChangeState(transferTriggered: true))
335                             {
336                                 //Last byte reception/transmission or lack of target may cause immediate change to the Idle state
337                                 TryChangeState(transferTriggered: false);
338                             }
339                         }
340                     )
341                     .WithWriteCallback((_, __) => UpdateInterrupts())
342                 },
343                 {(long)Registers.TransferData, new DoubleWordRegister(this)
344                     .WithReservedBits(8, 24)
345                     .WithValueField(0, 8, name: "transferData",
346                         writeCallback: (_, val) => EnqueueTx((byte)val),
347                         valueProviderCallback: (_) => DequeueRx()
348                     )
349                     .WithWriteCallback((_, __) => UpdateInterrupts())
350                 },
351                 {(long)Registers.InterruptStatus, new DoubleWordRegister(this)
352                     .WithReservedBits(10, 22)
353                     .WithTaggedFlag("arbitrationLostStatus", 9)
354                     .WithReservedBits(8, 1)
355                     .WithFlag(7,
356                         valueProviderCallback: (_) => rxFifoUnderflow.StickyStatus,
357                         writeCallback: (_, val) => rxFifoUnderflow.ClearSticky(val),
358                         name: "rxFifoUnderflowStatus"
359                     )
360                     .WithFlag(6,
361                         valueProviderCallback: (_) => txFifoOverflow.StickyStatus,
362                         writeCallback: (_, val) => txFifoOverflow.ClearSticky(val),
363                         name: "txFifoOverflowStatus"
364                     )
365                     .WithFlag(5,
366                         valueProviderCallback: (_) => rxFifoOverflow.StickyStatus,
367                         writeCallback: (_, val) => rxFifoOverflow.ClearSticky(val),
368                         name: "rxFifoOverflowStatus"
369                     )
370                     .WithFlag(4,
371                         valueProviderCallback: (_) => targetReady.StickyStatus,
372                         writeCallback: (_, val) => targetReady.ClearSticky(val),
373                         name: "targetReadyStatus"
374                     )
375                     .WithTaggedFlag("timeoutStatus", 3)
376                     .WithFlag(2,
377                         valueProviderCallback: (_) => transferNotAcknowledged.StickyStatus,
378                         writeCallback: (_, val) => transferNotAcknowledged.ClearSticky(val),
379                         name: "transferNotAcknowledgedStatus"
380                     )
381                     .WithFlag(1,
382                         valueProviderCallback: (_) => transferNewData.StickyStatus,
383                         writeCallback: (_, val) => transferNewData.ClearSticky(val),
384                         name: "transferNewDataStatus"
385                     )
386                     .WithFlag(0,
387                         valueProviderCallback: (_) => transferCompleted.StickyStatus,
388                         writeCallback: (_, val) => transferCompleted.ClearSticky(val),
389                         name: "transferCompletedStatus"
390                     )
391                     .WithWriteCallback((_, __) => UpdateInterrupts())
392                 },
393                 {(long)Registers.TransferSize, new DoubleWordRegister(this)
394                     .WithReservedBits(8, 24)
395                     .WithValueField(0, 8, out transferSize,
396                         changeCallback: (_, __) =>
397                         {
398                             if(transferDirection.Value == TransferDirection.Transmit)
399                             {
400                                 this.Log(LogLevel.Warning, "Changing transfer size while the transfer direction is set to transmit.");
401                             }
402                         })
403                 },
404                 {(long)Registers.InterruptMask, new DoubleWordRegister(this)
405                     .WithReservedBits(10, 22)
406                     .WithTaggedFlag("arbitrationLostMask", 9)
407                     .WithReservedBits(8, 1)
408                     .WithFlag(7, FieldMode.Read,
409                         valueProviderCallback: (_) => !rxFifoUnderflow.InterruptMask,
410                         name: "rxFifoUnderflowMask"
411                     )
412                     .WithFlag(6, FieldMode.Read,
413                         valueProviderCallback: (_) => !txFifoOverflow.InterruptMask,
414                         name: "txFifoOverflowMask"
415                     )
416                     .WithFlag(5, FieldMode.Read,
417                         valueProviderCallback: (_) => !rxFifoOverflow.InterruptMask,
418                         name: "rxFifoOverflowMask"
419                     )
420                     .WithFlag(4, FieldMode.Read,
421                         valueProviderCallback: (_) => !targetReady.InterruptMask,
422                         name: "targetReadyMask"
423                     )
424                     .WithTaggedFlag("timeoutMask", 3)
425                     .WithFlag(2, FieldMode.Read,
426                         valueProviderCallback: (_) => !transferNotAcknowledged.InterruptMask,
427                         name: "transferNotAcknowledgedMask"
428                     )
429                     .WithFlag(1, FieldMode.Read,
430                         valueProviderCallback: (_) => !transferNewData.InterruptMask,
431                         name: "transferNewDataMask"
432                     )
433                     .WithFlag(0, FieldMode.Read,
434                         valueProviderCallback: (_) => !transferCompleted.InterruptMask,
435                         name: "transferCompletedMask"
436                     )
437                 },
438                 {(long)Registers.InterruptEnable, new DoubleWordRegister(this)
439                     .WithReservedBits(10, 22)
440                     .WithTaggedFlag("arbitrationLostInterruptEnable", 9)
441                     .WithReservedBits(8, 1)
442                     .WithFlag(7, FieldMode.Write,
443                         writeCallback: (_, val) => rxFifoUnderflow.InterruptEnable(val),
444                         name: "rxFifoUnderflowInterruptEnable"
445                     )
446                     .WithFlag(6, FieldMode.Write,
447                         writeCallback: (_, val) => txFifoOverflow.InterruptEnable(val),
448                         name: "txFifoOverflowInterruptEnable"
449                     )
450                     .WithFlag(5, FieldMode.Write,
451                         writeCallback: (_, val) => rxFifoOverflow.InterruptEnable(val),
452                         name: "rxFifoOverflowInterruptEnable"
453                     )
454                     .WithFlag(4, FieldMode.Write,
455                         writeCallback: (_, val) => targetReady.InterruptEnable(val),
456                         name: "targetReadyInterruptEnable"
457                     )
458                     .WithTaggedFlag("timeoutInterruptEnable", 3)
459                     .WithFlag(2, FieldMode.Write,
460                         writeCallback: (_, val) => transferNotAcknowledged.InterruptEnable(val),
461                         name: "transferNotAcknowledgedInterruptEnable"
462                     )
463                     .WithFlag(1, FieldMode.Write,
464                         writeCallback: (_, val) => transferNewData.InterruptEnable(val),
465                         name: "transferNewDataInterruptEnable"
466                     )
467                     .WithFlag(0, FieldMode.Write,
468                         writeCallback: (_, val) => transferCompleted.InterruptEnable(val),
469                         name: "transferCompletedInterruptEnable"
470                     )
471                     .WithWriteCallback((_, __) => UpdateInterrupts())
472                 },
473                 {(long)Registers.InterruptDisable, new DoubleWordRegister(this)
474                     .WithReservedBits(10, 22)
475                     .WithTaggedFlag("arbitrationLostInterruptDisable", 9)
476                     .WithReservedBits(8, 1)
477                     .WithFlag(7, FieldMode.Write,
478                         writeCallback: (_, val) => rxFifoUnderflow.InterruptDisable(val),
479                         name: "rxFifoUnderflowInterruptDisable"
480                     )
481                     .WithFlag(6, FieldMode.Write,
482                         writeCallback: (_, val) => txFifoOverflow.InterruptDisable(val),
483                         name: "txFifoOverflowInterruptDisable"
484                     )
485                     .WithFlag(5, FieldMode.Write,
486                         writeCallback: (_, val) => rxFifoOverflow.InterruptDisable(val),
487                         name: "rxFifoOverflowInterruptDisable"
488                     )
489                     .WithFlag(4, FieldMode.Write,
490                         writeCallback: (_, val) => targetReady.InterruptDisable(val),
491                         name: "targetReadyInterruptDisable"
492                     )
493                     .WithTaggedFlag("timeoutInterruptDisable", 3)
494                     .WithFlag(2, FieldMode.Write,
495                         writeCallback: (_, val) => transferNotAcknowledged.InterruptDisable(val),
496                         name: "transferNotAcknowledgedInterruptDisable"
497                     )
498                     .WithFlag(1, FieldMode.Write,
499                         writeCallback: (_, val) => transferNewData.InterruptDisable(val),
500                         name: "transferNewDataInterruptDisable"
501                     )
502                     .WithFlag(0, FieldMode.Write,
503                         writeCallback: (_, val) => transferCompleted.InterruptDisable(val),
504                         name: "transferCompletedInterruptDisable"
505                     )
506                     .WithWriteCallback((_, __) => UpdateInterrupts())
507                 },
508             };
509         }
510 
GetInterruptFlags()511         private IEnumerable<CadenceInterruptFlag> GetInterruptFlags()
512         {
513             yield return txFifoOverflow;
514             yield return rxFifoOverflow;
515             yield return rxFifoUnderflow;
516             yield return targetReady;
517             yield return transferNotAcknowledged;
518             yield return transferNewData;
519             yield return transferCompleted;
520         }
521 
522         private int TransferAddress
523         {
524             get
525             {
526                 if(addressingMode.Value == AddressingMode.Extended)
527                 {
528                     return (int)transferAddressReg.Value & ExtendedAddressMask;
529                 }
530                 return (int)transferAddressReg.Value & NormalAddressMask;
531             }
532         }
533 
534         private II2CPeripheral targetDevice;
535         private TransferState transferState;
536 
537         private IFlagRegisterField targetMonitorEnabled;
538         private IFlagRegisterField transferHold;
539         private IEnumRegisterField<AddressingMode> addressingMode;
540         private IEnumRegisterField<TransferDirection> transferDirection;
541         private IValueRegisterField transferAddressReg;
542         private IValueRegisterField transferSize;
543 
544         private readonly CadenceInterruptFlag txFifoOverflow;
545         private readonly CadenceInterruptFlag rxFifoOverflow;
546         private readonly CadenceInterruptFlag rxFifoUnderflow;
547         private readonly CadenceInterruptFlag targetReady;
548         private readonly CadenceInterruptFlag transferNotAcknowledged;
549         private readonly CadenceInterruptFlag transferNewData;
550         private readonly CadenceInterruptFlag transferCompleted;
551 
552         private readonly Queue<byte> txFifo;
553         private readonly Queue<byte> txTransfer;
554         private readonly Queue<byte> rxFifo;
555         private readonly DoubleWordRegisterCollection registers;
556 
557         private const int FifoCapacity = 16;
558         private const int MaxTransferSize = 255;
559         private const int ExtendedAddressMask = 0x3FF;
560         private const int NormalAddressMask = 0x7F;
561 
562         private enum TransferState
563         {
564             Idle,
565             Transmitting,
566             Receiving
567         }
568 
569         private enum AddressingMode
570         {
571             Extended = 0x0,
572             Normal = 0x1
573         }
574 
575         private enum TransferDirection
576         {
577             Transmit = 0x0,
578             Receive = 0x1
579         }
580 
581         private enum Registers : long
582         {
583             Control = 0x00,
584             Status = 0x04,
585             TransferAddress = 0x08,
586             TransferData = 0x0c,
587             InterruptStatus = 0x10,
588             TransferSize = 0x14,
589             TargetMonitor = 0x18,
590             Timeout = 0x1c,
591             InterruptMask = 0x20,
592             InterruptEnable = 0x24,
593             InterruptDisable = 0x28,
594             GlitchFilter = 0x2c
595         }
596     }
597 }
598