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 Antmicro.Renode.Peripherals.Bus;
10 using Antmicro.Renode.Core.Structure.Registers;
11 using Antmicro.Renode.Core.Structure;
12 using Antmicro.Renode.Core;
13 using Antmicro.Renode.Logging;
14 using Antmicro.Renode.Time;
15 
16 // This model is a reimplementation of the OpenCoresI2C module.
17 namespace Antmicro.Renode.Peripherals.I2C
18 {
19     [AllowedTranslations(AllowedTranslation.ByteToDoubleWord)]
20     public class BetrustedSocI2C : SimpleContainer<II2CPeripheral>, IDoubleWordPeripheral, IKnownSize
21     {
BetrustedSocI2C(IMachine machine)22         public BetrustedSocI2C(IMachine machine) : base(machine)
23         {
24             dataToSlave = new Queue<byte>();
25             dataFromSlave = new Queue<byte>();
26             IRQ = new GPIO();
27 
28             irqTimeoutCallback = new ClockEntry(IrqTimeout, 1000, this.FinishTransaction, machine, "Irq Scheduler");
29 
30             var registersMap = new Dictionary<long, DoubleWordRegister>()
31             {
32                 {(long)Registers.ClockPrescale, new DoubleWordRegister(this)
33                     .WithReservedBits(16, 16)
34                     .WithValueField(0, 16, FieldMode.Read | FieldMode.Write)
35                 },
36 
37                 {(long)Registers.Control, new DoubleWordRegister(this)
38                     .WithReservedBits(8, 24)
39                     .WithFlag(7, out enabled)
40                     .WithTag("Interrupt enable", 6, 1)
41                     .WithReservedBits(0, 6)
42                 },
43 
44                 {(long)Registers.Transmit, new DoubleWordRegister(this)
45                     .WithReservedBits(8, 24)
46                     .WithValueField(0, 8, out transmitBuffer, FieldMode.Write)
47                 },
48                 {(long)Registers.Reset, new DoubleWordRegister(this)
49                     .WithReservedBits(1, 31)
50                     .WithFlag(0, writeCallback: (_, val) =>
51                     {
52                         if(val)
53                         {
54                             dataToSlave.Clear();
55                             dataFromSlave.Clear();
56                             UpdateInterrupts();
57                         }
58                     })
59                 },
60 
61                 {(long)Registers.Receive, new DoubleWordRegister(this)
62                     .WithReservedBits(8, 24)
63                     .WithValueField(0, 8, out receiveBuffer, FieldMode.Read)
64                 },
65 
66                 {(long)Registers.Status, new DoubleWordRegister(this)
67                     .WithReservedBits(8, 24)
68                     .WithFlag(7, out receivedAckFromSlaveNegated, FieldMode.Read)
69                     .WithFlag(6, FieldMode.Read, valueProviderCallback: _ => false, name: "Busy")
70                     .WithFlag(5, FieldMode.Read, valueProviderCallback: _ => false, name: "Arbitration lost")
71                     .WithReservedBits(2, 3)
72                     .WithFlag(1, FieldMode.Read, valueProviderCallback: _ => false, name: "Transfer in progress")
73                     .WithFlag(0, out i2cIntIrqStatus, FieldMode.Read)
74                 },
75 
76                 {(long)Registers.EventStatus, new DoubleWordRegister(this)
77                     .WithReservedBits(2, 30)
78                     .WithFlag(1, FieldMode.Read, name: "TX_RX_DONE_STATUS", valueProviderCallback: _ => txRxDoneIrqStatus)
79                     .WithFlag(0, FieldMode.Read, name: "I2C_INT_STATUS", valueProviderCallback: _ => i2cIntIrqStatus.Value)
80                 },
81 
82                 {(long)Registers.EventPending, new DoubleWordRegister(this)
83                     .WithReservedBits(2, 30)
84                     .WithFlag(1, out txRxDoneIrqPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "TX_RX_DONE_PENDING", changeCallback: (_, __) => UpdateInterrupts())
85                     .WithFlag(0, out i2cIntIrqPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "I2C_INT_PENDING", changeCallback: (_, __) => UpdateInterrupts())
86                 },
87 
88                 {(long)Registers.EventEnable, new DoubleWordRegister(this)
89                     .WithReservedBits(2, 30)
90                     .WithFlag(1, out txRxDoneIrqEnabled, name: "TX_RX_DONE_ENABLE", changeCallback: (_, __) => UpdateInterrupts())
91                     .WithFlag(0, out i2cIntIrqEnabled, name: "I2C_INT_ENABLE", changeCallback: (_, __) => UpdateInterrupts())
92                 },
93 
94                 {(long)Registers.Command, new DoubleWordRegister(this)
95                     .WithReservedBits(8, 24)
96                     .WithFlag(7, out generateStartCondition, FieldMode.Write)
97                     .WithFlag(6, out generateStopCondition, FieldMode.Write)
98                     .WithFlag(5, out readFromSlave, FieldMode.Write)
99                     .WithFlag(4, out writeToSlave, FieldMode.Write)
100                     .WithFlag(3, FieldMode.Read, name: "ACK", valueProviderCallback: _ => true)
101                     // .WithTag("ACK", 3, 1)
102                     .WithReservedBits(1, 2)
103                     .WithFlag(0, FieldMode.Write, writeCallback: (_, __) => i2cIntIrqStatus.Value = false)
104                     .WithWriteCallback((_, __) =>
105                     {
106                         if(!enabled.Value)
107                         {
108                             return;
109                         }
110 
111                         if(generateStartCondition.Value)
112                         {
113                             generateStartCondition.Value = false;
114                             if(transactionInProgress)
115                             {
116                                 // repeated start - finish previous transaction first
117                                 SendDataToSlave();
118                             }
119                             else
120                             {
121                                 transactionInProgress = true;
122                             }
123 
124                             dataFromSlave.Clear();
125                             selectedSlave?.FinishTransmission();
126 
127                             if(!TryResolveSelectedSlave(out selectedSlave))
128                             {
129                                 return;
130                             }
131                         }
132 
133                         if(writeToSlave.Value)
134                         {
135                             writeToSlave.Value = false;
136                             HandleWriteToSlaveCommand();
137                         }
138 
139                         if(readFromSlave.Value)
140                         {
141                             readFromSlave.Value = false;
142                             HandleReadFromSlaveCommand();
143                         }
144 
145                         if (transactionInProgress) {
146                             machine.ClockSource.AddClockEntry(irqTimeoutCallback);
147                         }
148 
149                         if(generateStopCondition.Value)
150                         {
151                             selectedSlave.FinishTransmission();
152 
153                             generateStopCondition.Value = false;
154                             if(!transactionInProgress)
155                             {
156                                 return;
157                             }
158 
159                             SendDataToSlave();
160                             transactionInProgress = false;
161                         }
162 
163                         shouldSendTxRxIrq = true;
164                     })
165                 }
166             };
167 
168             registers = new DoubleWordRegisterCollection(this, registersMap);
169             UpdateInterrupts();
170         }
171 
FinishTransaction()172         private void FinishTransaction()
173         {
174             machine.ClockSource.TryRemoveClockEntry(FinishTransaction);
175             if(shouldSendTxRxIrq)
176             {
177                 shouldSendTxRxIrq = false;
178                 txRxDoneIrqStatus = true;
179                 UpdateInterrupts();
180                 txRxDoneIrqStatus = false;
181             }
182         }
183 
Reset()184         public override void Reset()
185         {
186             registers.Reset();
187             dataToSlave.Clear();
188             dataFromSlave.Clear();
189             UpdateInterrupts();
190         }
191 
ReadDoubleWord(long offset)192         public uint ReadDoubleWord(long offset)
193         {
194             return registers.Read(offset);
195         }
196 
WriteDoubleWord(long offset, uint value)197         public void WriteDoubleWord(long offset, uint value)
198         {
199             registers.Write(offset, value);
200         }
201 
UpdateInterrupts()202         private void UpdateInterrupts()
203         {
204             if(i2cIntIrqStatus.Value && i2cIntIrqEnabled.Value)
205             {
206                 i2cIntIrqPending.Value = true;
207             }
208             if(txRxDoneIrqStatus && txRxDoneIrqEnabled.Value)
209             {
210                 txRxDoneIrqPending.Value = true;
211             }
212             // this.Log(LogLevel.Noisy, "    Setting status: {0}, enabled: {1}, pending: {2}", txRxDoneIrqStatus, txRxDoneIrqEnabled.Value, txRxDoneIrqPending.Value);
213             IRQ.Set(i2cIntIrqPending.Value || txRxDoneIrqPending.Value);
214         }
215 
216         public GPIO IRQ { get; }
217 
218         public long Size => 0x1000;
219 
TryResolveSelectedSlave(out II2CPeripheral selectedSlave)220         private bool TryResolveSelectedSlave(out II2CPeripheral selectedSlave)
221         {
222             var slaveAddress = (byte)(transmitBuffer.Value >> 1);
223             if(!ChildCollection.TryGetValue(slaveAddress, out selectedSlave))
224             {
225                 this.Log(LogLevel.Warning, "Addressing unregistered slave: 0x{0:X}", slaveAddress);
226                 receivedAckFromSlaveNegated.Value = true;
227                 return false;
228             }
229 
230             receivedAckFromSlaveNegated.Value = false;
231             return true;
232         }
233 
HandleReadFromSlaveCommand()234         private void HandleReadFromSlaveCommand()
235         {
236             if(dataFromSlave.Count == 0)
237             {
238                 foreach(var b in selectedSlave.Read())
239                 {
240                     dataFromSlave.Enqueue(b);
241                 }
242 
243                 if(dataFromSlave.Count == 0)
244                 {
245                     this.Log(LogLevel.Warning, "Trying to read from slave, but no data is available");
246                     receiveBuffer.Value = 0;
247                     return;
248                 }
249             }
250 
251             receiveBuffer.Value = dataFromSlave.Dequeue();
252             UpdateInterrupts();
253         }
254 
SendDataToSlave()255         private void SendDataToSlave()
256         {
257             if(dataToSlave.Count == 0 || selectedSlave == null)
258             {
259                 this.Log(LogLevel.Warning, "Trying to send data to slave, but either no data is available or the slave is not selected");
260                 return;
261             }
262 
263             selectedSlave.Write(dataToSlave.ToArray());
264             dataToSlave.Clear();
265         }
266 
HandleWriteToSlaveCommand()267         private void HandleWriteToSlaveCommand()
268         {
269             if(!transactionInProgress)
270             {
271                 this.Log(LogLevel.Warning, "Writing to slave without generating START signal");
272                 return;
273             }
274 
275             dataToSlave.Enqueue((byte)transmitBuffer.Value);
276             i2cIntIrqStatus.Value = true;
277             UpdateInterrupts();
278         }
279 
280         private bool transactionInProgress;
281         private II2CPeripheral selectedSlave;
282 
283         private readonly Queue<byte> dataToSlave;
284         private readonly Queue<byte> dataFromSlave;
285         private readonly IValueRegisterField receiveBuffer;
286         private readonly IValueRegisterField transmitBuffer;
287         private readonly IFlagRegisterField readFromSlave;
288         private readonly IFlagRegisterField writeToSlave;
289         private readonly IFlagRegisterField i2cIntIrqStatus;
290         private IFlagRegisterField i2cIntIrqEnabled;
291         private IFlagRegisterField i2cIntIrqPending;
292         private bool txRxDoneIrqStatus;
293         private bool shouldSendTxRxIrq;
294         private IFlagRegisterField txRxDoneIrqEnabled;
295         private IFlagRegisterField txRxDoneIrqPending;
296         private readonly IFlagRegisterField enabled;
297         private readonly IFlagRegisterField receivedAckFromSlaveNegated;
298         private readonly IFlagRegisterField generateStartCondition;
299         private readonly IFlagRegisterField generateStopCondition;
300         private readonly DoubleWordRegisterCollection registers;
301         private ClockEntry irqTimeoutCallback;
302 
303         private const int IrqTimeout = 5;
304 
305         private enum Registers
306         {
307             ClockPrescale = 0x0,
308             Control = 0x4,
309             Transmit = 0x8,
310             Receive = 0xC,
311             Command = 0x10,
312             Status = 0x14,
313             Reset = 0x18,
314             EventStatus = 0x1c,
315             EventPending = 0x20,
316             EventEnable = 0x24
317         }
318     }
319 }
320