1 //
2 // Copyright (c) 2010-2023 Antmicro
3 // Copyright (c) 2021-2023 Precursor Project
4 //
5 // This file is licensed under the MIT License.
6 // Full license text is available in 'licenses/MIT.txt'.
7 //
8 using System.Collections.Generic;
9 using System;
10 using Antmicro.Renode.Peripherals.Bus;
11 using Antmicro.Renode.Core.Structure.Registers;
12 using Antmicro.Renode.Core.Structure;
13 using Antmicro.Renode.Core;
14 using Antmicro.Renode.Logging;
15 using Antmicro.Renode.Time;
16 using System.Threading;
17 
18 // This model is a reimplementation of the OpenCoresI2C module.
19 namespace Antmicro.Renode.Peripherals.I2C
20 {
21     [AllowedTranslations(AllowedTranslation.ByteToDoubleWord)]
22     public class BetrustedEcI2C : SimpleContainer<II2CPeripheral>, IDoubleWordPeripheral, IKnownSize
23     {
BetrustedEcI2C(IMachine machine)24         public BetrustedEcI2C(IMachine machine) : base(machine)
25         {
26             dataToSlave = new Queue<byte>();
27             dataFromSlave = new Queue<byte>();
28             IRQ = new GPIO();
29 
30             irqTimeoutCallback = new ClockEntry(IrqTimeout, 1000, this.FinishTransaction, machine, "Irq Scheduler");
31 
32             var registersMap = new Dictionary<long, DoubleWordRegister>()
33             {
34                 {(long)Registers.Prescale, new DoubleWordRegister(this)
35                     .WithReservedBits(16, 16)
36                     .WithValueField(0, 16, FieldMode.Read | FieldMode.Write)
37                 },
38 
39                 {(long)Registers.Control, new DoubleWordRegister(this)
40                     .WithReservedBits(8, 24)
41                     .WithFlag(7, out enabled)
42                     .WithTag("Interrupt enable", 6, 1)
43                     .WithReservedBits(0, 6)
44                 },
45 
46                 {(long)Registers.Txr, new DoubleWordRegister(this)
47                     .WithReservedBits(8, 24)
48                     .WithValueField(0, 8, out transmitBuffer, FieldMode.Write)
49                 },
50 
51                 {(long)Registers.Rxr, new DoubleWordRegister(this)
52                     .WithReservedBits(8, 24)
53                     .WithValueField(0, 8, out receiveBuffer, FieldMode.Read)
54                 },
55 
56                 {(long)Registers.Status, new DoubleWordRegister(this)
57                     .WithReservedBits(8, 24)
58                     .WithFlag(7, out receivedAckFromSlaveNegated, FieldMode.Read)
59                     .WithFlag(6, FieldMode.Read, valueProviderCallback: _ => false, name: "Busy")
60                     .WithFlag(5, FieldMode.Read, valueProviderCallback: _ => false, name: "Arbitration lost")
61                     .WithReservedBits(2, 3)
62                     .WithFlag(1, name: "Transfer in progress", valueProviderCallback: _ =>
63                     {
64                         fakeTip = !fakeTip;
65                         return fakeTip;
66                     })
67                     .WithFlag(0, out i2cIrqStatus, FieldMode.Read)
68                 },
69 
70                 {(long)Registers.Command, new DoubleWordRegister(this)
71                     .WithReservedBits(8, 24)
72                     .WithFlag(7, out generateStartCondition, FieldMode.Write)
73                     .WithFlag(6, out generateStopCondition, FieldMode.Write)
74                     .WithFlag(5, out readFromSlave, FieldMode.Write)
75                     .WithFlag(4, out writeToSlave, FieldMode.Write)
76                     .WithFlag(3, FieldMode.Read, name: "ACK", valueProviderCallback: _ => true)
77                     .WithReservedBits(1, 2)
78                     .WithFlag(0, FieldMode.Write, writeCallback: (_, __) => i2cIrqStatus.Value = false)
79                     .WithWriteCallback((_, __) =>
80                     {
81                         if(!enabled.Value)
82                         {
83                             return;
84                         }
85 
86                         if(generateStartCondition.Value)
87                         {
88                             generateStartCondition.Value = false;
89                             if(transactionInProgress)
90                             {
91                                 // repeated start - finish previous transaction first
92                                 SendDataToSlave();
93                             }
94                             else
95                             {
96                                 transactionInProgress = true;
97                             }
98 
99                             dataFromSlave.Clear();
100 
101                             selectedSlave?.FinishTransmission();
102                             if(!TryResolveSelectedSlave(out selectedSlave))
103                             {
104                                 return;
105                             }
106                         }
107 
108                         if(writeToSlave.Value)
109                         {
110                             writeToSlave.Value = false;
111                             HandleWriteToSlaveCommand();
112                         }
113 
114                         if(readFromSlave.Value)
115                         {
116                             readFromSlave.Value = false;
117                             HandleReadFromSlaveCommand();
118                         }
119 
120                         if (transactionInProgress && 0 == Interlocked.CompareExchange(ref irqTimeoutCallbackQueued, 1, 0)) {
121                             // this.Log(LogLevel.Error, "I2C: Adding clock entry for {0}",this.irqTimeoutCallback);
122                             try {
123                                 machine.ClockSource.AddClockEntry(irqTimeoutCallback);
124                             }
125                             catch (ArgumentException ex)
126                             {
127                                 this.Log(LogLevel.Error, "Unable to add clock entry for timeout (queued: {0}): {1}", ex, irqTimeoutCallbackQueued);
128                                 // this.Log(LogLevel.Error, "I2C: Done adding clock entry for {0}",this.irqTimeoutCallback);
129                             }
130                         }
131 
132                         if(generateStopCondition.Value)
133                         {
134                             selectedSlave?.FinishTransmission();
135                             generateStopCondition.Value = false;
136                             if(!transactionInProgress)
137                             {
138                                 return;
139                             }
140 
141                             SendDataToSlave();
142                             transactionInProgress = false;
143                         }
144 
145                         shouldSendTxRxIrq = true;
146                     })
147                 },
148                 {(long)Registers.EventStatus, new DoubleWordRegister(this)
149                     .WithReservedBits(4, 28)
150                     .WithFlag(3, FieldMode.Read, name: "USBCC_INT_STATUS", valueProviderCallback: _ => false /*usbccIrqStatus*/)
151                     .WithFlag(2, FieldMode.Read, name: "GYRO_INT_STATUS", valueProviderCallback: _ => false /*gyroIrqStatus*/)
152                     .WithFlag(1, FieldMode.Read, name: "GG_INT_STATUS", valueProviderCallback: _ => false /*ggIrqStatus*/)
153                     .WithFlag(0, FieldMode.Read, name: "I2C_INT_STATUS", valueProviderCallback: _ => i2cIrqStatus.Value)
154                 },
155 
156                 {(long)Registers.EventPending, new DoubleWordRegister(this)
157                     .WithReservedBits(4, 28)
158                     .WithFlag(3, out usbccIrqPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "USBCC_INT_PENDING", changeCallback: (_, __) => UpdateInterrupts())
159                     .WithFlag(2, out gyroIrqPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "GYRO_INT_PENDING", changeCallback: (_, __) => UpdateInterrupts())
160                     .WithFlag(1, out ggIrqPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "GG_INT_PENDING", changeCallback: (_, __) => UpdateInterrupts())
161                     .WithFlag(0, out i2cIrqPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "I2C_INT_PENDING", changeCallback: (_, __) => UpdateInterrupts())
162                 },
163 
164                 {(long)Registers.EventEnable, new DoubleWordRegister(this)
165                     .WithReservedBits(4, 28)
166                     .WithFlag(3, out usbccIrqEnabled, name: "USBCC_INT_ENABLE", changeCallback: (_, __) => UpdateInterrupts())
167                     .WithFlag(2, out gyroIrqEnabled, name: "GYRO_INT_ENABLE", changeCallback: (_, __) => UpdateInterrupts())
168                     .WithFlag(1, out ggIrqEnabled, name: "GG_INT_ENABLE", changeCallback: (_, __) => UpdateInterrupts())
169                     .WithFlag(0, out i2cIrqEnabled, name: "I2C_INT_ENABLE", changeCallback: (_, __) => UpdateInterrupts())
170                 }
171             };
172 
173             registers = new DoubleWordRegisterCollection(this, registersMap);
174             UpdateInterrupts();
175         }
176 
FinishTransaction()177         private void FinishTransaction()
178         {
179             // this.Log(LogLevel.Error, "I2C: Removing clock entry for {0}",this.irqTimeoutCallback);
180             machine.ClockSource.TryRemoveClockEntry(FinishTransaction);
181             irqTimeoutCallbackQueued = 0;
182             if(shouldSendTxRxIrq)
183             {
184                 shouldSendTxRxIrq = false;
185                 // txRxDoneIrqStatus = true;
186                 UpdateInterrupts();
187                 // txRxDoneIrqStatus = false;
188             }
189         }
190 
Reset()191         public override void Reset()
192         {
193             registers.Reset();
194             dataToSlave.Clear();
195             dataFromSlave.Clear();
196             UpdateInterrupts();
197         }
198 
ReadDoubleWord(long offset)199         public uint ReadDoubleWord(long offset)
200         {
201             return registers.Read(offset);
202         }
203 
WriteDoubleWord(long offset, uint value)204         public void WriteDoubleWord(long offset, uint value)
205         {
206             registers.Write(offset, value);
207         }
208 
UpdateInterrupts()209         private void UpdateInterrupts()
210         {
211             if(i2cIrqStatus.Value)
212             {
213                 i2cIrqPending.Value = true;
214             }
215             /*
216             if(ggIrqStatus)
217             {
218                 ggIrqPending.Value = true;
219             }
220             if(gyroIrqStatus)
221             {
222                 gyroIrqPending.Value = true;
223             }
224             if(usbccIrqStatus)
225             {
226                 usbccIrqPending.Value = true;
227             }
228             */
229             // this.Log(LogLevel.Noisy, "    Setting status: {0}, enabled: {1}, pending: {2}", txRxDoneIrqStatus, txRxDoneIrqEnabled.Value, txRxDoneIrqPending.Value);
230             IRQ.Set((i2cIrqPending.Value && i2cIrqEnabled.Value)
231                 || (ggIrqPending.Value && ggIrqEnabled.Value)
232                 || (gyroIrqPending.Value && gyroIrqEnabled.Value)
233                 || (usbccIrqPending.Value && usbccIrqEnabled.Value));
234         }
235 
236         public GPIO IRQ { get; }
237 
238         public long Size => 0x800;
239 
TryResolveSelectedSlave(out II2CPeripheral selectedSlave)240         private bool TryResolveSelectedSlave(out II2CPeripheral selectedSlave)
241         {
242             var slaveAddress = (byte)(transmitBuffer.Value >> 1);
243             if(!ChildCollection.TryGetValue(slaveAddress, out selectedSlave))
244             {
245                 this.Log(LogLevel.Warning, "Addressing unregistered slave: 0x{0:X}", slaveAddress);
246                 receivedAckFromSlaveNegated.Value = true;
247                 return false;
248             }
249 
250             receivedAckFromSlaveNegated.Value = false;
251             return true;
252         }
253 
HandleReadFromSlaveCommand()254         private void HandleReadFromSlaveCommand()
255         {
256             if (dataFromSlave == null)
257             {
258                 this.Log(LogLevel.Error, "dataFromSlave is NULL!");
259                 return;
260             }
261             if(selectedSlave == null)
262             {
263                 this.Log(LogLevel.Error, "selectedSlave is NULL!");
264                 return;
265             }
266 
267             foreach(var b in selectedSlave.Read())
268             {
269                 dataFromSlave.Enqueue(b);
270             }
271 
272             if(dataFromSlave.Count == 0)
273             {
274                 this.Log(LogLevel.Warning, "Trying to read from slave, but no data is available");
275                 receiveBuffer.Value = 0;
276                 return;
277             }
278 
279             if (receiveBuffer == null)
280             {
281                 this.Log(LogLevel.Error, "receiveBuffer is NULL!");
282                 dataFromSlave.Dequeue();
283             }
284             receiveBuffer.Value = dataFromSlave.Dequeue();
285             UpdateInterrupts();
286         }
287 
SendDataToSlave()288         private void SendDataToSlave()
289         {
290             if(dataToSlave.Count == 0 || selectedSlave == null)
291             {
292                 this.Log(LogLevel.Warning, "Trying to send data to slave, but either no data is available or the slave is not selected");
293                 return;
294             }
295 
296             selectedSlave.Write(dataToSlave.ToArray());
297             dataToSlave.Clear();
298         }
299 
HandleWriteToSlaveCommand()300         private void HandleWriteToSlaveCommand()
301         {
302             if(!transactionInProgress)
303             {
304                 this.Log(LogLevel.Warning, "Writing to slave without generating START signal");
305                 return;
306             }
307 
308             dataToSlave.Enqueue((byte)transmitBuffer.Value);
309             i2cIrqStatus.Value = true;
310             UpdateInterrupts();
311         }
312 
313         private bool transactionInProgress;
314         // Just toggle TIP here to fake it.
315         private bool fakeTip;
316         private II2CPeripheral selectedSlave;
317 
318         private readonly Queue<byte> dataToSlave;
319         private readonly Queue<byte> dataFromSlave;
320         private readonly IValueRegisterField receiveBuffer;
321         private readonly IValueRegisterField transmitBuffer;
322         private readonly IFlagRegisterField readFromSlave;
323         private readonly IFlagRegisterField writeToSlave;
324 
325         private IFlagRegisterField i2cIrqEnabled;
326         private IFlagRegisterField ggIrqEnabled;
327         private IFlagRegisterField gyroIrqEnabled;
328         private IFlagRegisterField usbccIrqEnabled;
329         private IFlagRegisterField i2cIrqStatus;
330         // private bool ggIrqStatus;
331         // private bool gyroIrqStatus;
332         // private bool usbccIrqStatus;
333         private IFlagRegisterField i2cIrqPending;
334         private IFlagRegisterField ggIrqPending;
335         private IFlagRegisterField gyroIrqPending;
336         private IFlagRegisterField usbccIrqPending;
337 
338         private bool shouldSendTxRxIrq;
339         // private bool txRxDoneIrqStatus;
340         private long irqTimeoutCallbackQueued;
341         private readonly IFlagRegisterField enabled;
342         private readonly IFlagRegisterField receivedAckFromSlaveNegated;
343         private readonly IFlagRegisterField generateStartCondition;
344         private readonly IFlagRegisterField generateStopCondition;
345         private readonly DoubleWordRegisterCollection registers;
346         private ClockEntry irqTimeoutCallback;
347 
348         private const ulong IrqTimeout = 5;
349 
350         private enum Registers
351         {
352             Prescale = 0x0,
353             Control = 0x4,
354             Txr = 0x8,
355             Rxr = 0xC,
356             Command = 0x10,
357             Status = 0x14,
358             BitbangMode = 0x18,
359             Bb = 0x1c,
360             BbR = 0x20,
361             EventStatus = 0x24,
362             EventPending = 0x28,
363             EventEnable = 0x2c
364         }
365     }
366 }
367