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.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.Peripherals.Bus;
14 using Antmicro.Renode.Peripherals.SPI;
15 using Antmicro.Renode.Logging;
16 using Antmicro.Renode.Exceptions;
17 using Antmicro.Renode.Utilities;
18 
19 namespace Antmicro.Renode.Peripherals.I2C
20 {
21     public class MAX32650_I2C : SimpleContainer<II2CPeripheral>, IDoubleWordPeripheral, IKnownSize
22     {
MAX32650_I2C(IMachine machine)23         public MAX32650_I2C(IMachine machine) : base(machine)
24         {
25             IRQ = new GPIO();
26 
27             registers = new DoubleWordRegisterCollection(this, BuildRegisterMap());
28             txQueue = new Queue<byte>();
29             rxQueue = new Queue<byte>();
30         }
31 
Reset()32         public override void Reset()
33         {
34             state = States.Idle;
35             destinationAddress = 0;
36             bytesToReceive = 0;
37             enabled = false;
38 
39             registers.Reset();
40             txQueue.Clear();
41             rxQueue.Clear();
42             IRQ.Unset();
43         }
44 
ReadDoubleWord(long offset)45         public uint ReadDoubleWord(long offset)
46         {
47             return registers.Read(offset);
48         }
49 
WriteDoubleWord(long offset, uint value)50         public void WriteDoubleWord(long offset, uint value)
51         {
52             registers.Write(offset, value);
53         }
54 
55         public long Size => 0x1000;
56 
57         public GPIO IRQ { get; }
58 
HandleTransaction()59         private void HandleTransaction()
60         {
61             if(!enabled)
62             {
63                 // We are ignoring calls to HandleTransaction when I2C peripheral is disabled
64                 return;
65             }
66 
67             switch(state)
68             {
69                 case States.Ready:
70                     // We are being called as a result of START condition
71                     while(state == States.Ready || state == States.WaitForAddress)
72                     {
73                         if(txQueue.TryDequeue(out var data))
74                         {
75                             HandleWriteByte(data);
76                         }
77                         else
78                         {
79                             state = States.Idle;
80                             break;
81                         }
82                     }
83                     break;
84 
85                 case States.Writing:
86                     // We are being called as a result of RESTART/STOP condition
87                     CurrentSlave.Write(txQueue.ToArray());
88                     txQueue.Clear();
89                     interruptDonePending.Value = true;
90                     break;
91 
92                 default:
93                     // We don't have any writes to do; ignore
94                     break;
95             }
96 
97             UpdateInterrupts();
98         }
99 
HandleWriteByte(byte data)100         private void HandleWriteByte(byte data)
101         {
102             if(!enabled)
103             {
104                 this.Log(LogLevel.Warning, "Trying to write byte to FIFO, but peripheral is disabled");
105                 return;
106             }
107 
108             switch(state)
109             {
110                 case States.Idle:
111                 case States.Writing:
112                     txQueue.Enqueue(data);
113                     break;
114 
115                 case States.Ready:
116                     if(slaveExtendedAddress.Value)
117                     {
118                         destinationAddress = (uint)data & 0x3;
119                         state = States.WaitForAddress;
120                     }
121                     else
122                     {
123                         state = ((data & 0x1) != 0) ? States.Reading : States.Writing;
124                         destinationAddress = (uint)(data >> 1) & 0x7F;
125 
126                         if(CurrentSlave == null)
127                         {
128                             this.Log(LogLevel.Warning, "Trying to access a peripheral at an address 0x{0:X02}, but no such peripheral is connected", destinationAddress);
129                             interruptTimeoutPending.Value = true;
130                             state = States.Idle;
131                             txQueue.Clear();
132                         }
133                     }
134 
135                     TryReadingToBuffer();
136                     break;
137 
138                 case States.WaitForAddress:
139                     state = ((destinationAddress & 0x1) != 0) ? States.Reading : States.Writing;
140                     destinationAddress = (destinationAddress & 0x6) << 7;
141                     destinationAddress |= data;
142 
143                     if(CurrentSlave == null)
144                     {
145                         this.Log(LogLevel.Warning, "Trying to access a peripheral at an address 0x{0:X03}, but no such peripheral is connected", destinationAddress);
146                         interruptTimeoutPending.Value = true;
147                         state = States.Idle;
148                         txQueue.Clear();
149                     }
150 
151                     TryReadingToBuffer();
152                     break;
153 
154                 case States.ReadingFromBuffer:
155                     this.Log(LogLevel.Warning, "Writing data to FIFO while in reading state, ignoring incoming data.");
156                     break;
157 
158                 default:
159                     throw new Exception($"Unhandled state in HandleWriteByte: {state}");
160             }
161 
162             UpdateInterrupts();
163         }
164 
HandleReadByte()165         private byte HandleReadByte()
166         {
167             if(!enabled)
168             {
169                 this.Log(LogLevel.Error, "Trying to read byte from FIFO, but peripheral is disabled");
170                 return DefaultReturnValue;
171             }
172 
173             byte result = DefaultReturnValue;
174             if(rxQueue.TryDequeue(out var data))
175             {
176                 result = data;
177             }
178             else
179             {
180                 interruptDonePending.Value = true;
181             }
182 
183             UpdateInterrupts();
184             return result;
185         }
186 
TryReadingToBuffer()187         private void TryReadingToBuffer()
188         {
189             if(state != States.Reading)
190             {
191                 return;
192             }
193 
194             if(rxQueue.Count > 0)
195             {
196                 this.Log(LogLevel.Warning, "Receiving new data with non-empty RX queue");
197             }
198 
199             var receivedBytes = CurrentSlave.Read(bytesToReceive);
200             if(receivedBytes.Length != bytesToReceive)
201             {
202                 this.Log(LogLevel.Warning, "Requested {0} bytes, but received {1}", bytesToReceive, rxQueue.Count);
203             }
204 
205             foreach(var value in receivedBytes)
206             {
207                 rxQueue.Enqueue(value);
208             }
209 
210             state = States.ReadingFromBuffer;
211         }
212 
UpdateInterrupts()213         private void UpdateInterrupts()
214         {
215             interruptRxThresholdPending.Value = rxQueue.Count >= (int)rxThreshold.Value;
216             interruptTxThresholdPending.Value = txQueue.Count <= (int)txThreshold.Value;
217 
218             var pending = false;
219             pending |= interruptTimeoutEnabled.Value && interruptTimeoutPending.Value;
220             pending |= interruptRxThresholdEnabled.Value && interruptRxThresholdPending.Value;
221             pending |= interruptTxThresholdEnabled.Value && interruptTxThresholdPending.Value;
222             pending |= interruptStopEnabled.Value && interruptStopPending.Value;
223             pending |= interruptDoneEnabled.Value && interruptDonePending.Value;
224             IRQ.Set(pending);
225         }
226 
BuildRegisterMap()227         private Dictionary<long, DoubleWordRegister> BuildRegisterMap()
228         {
229             var registerMap = new Dictionary<long, DoubleWordRegister>()
230             {
231                 {(long)Registers.Status, new DoubleWordRegister(this)
232                     .WithFlag(0, FieldMode.Read, name: "STAT.busy",
233                         valueProviderCallback: _ => state != States.Idle)
234                     .WithFlag(1, FieldMode.Read, name: "STAT.rxe",
235                         valueProviderCallback: _ => rxQueue.Count == 0)
236                     .WithFlag(2, FieldMode.Read, name: "STAT.rxf",
237                         valueProviderCallback: _ => false)
238                     .WithFlag(3, FieldMode.Read, name: "STAT.txe",
239                         valueProviderCallback: _ => true)
240                     .WithFlag(4, FieldMode.Read, name: "STAT.txf",
241                         valueProviderCallback: _ => false)
242                     .WithFlag(5, FieldMode.Read, name: "STAT.chmd",
243                         valueProviderCallback: _ => state != States.Idle)
244                     .WithReservedBits(6, 26)
245                 },
246                 {(long)Registers.Control0, new DoubleWordRegister(this)
247                     .WithFlag(0, name: "CTRL0.i2cen",
248                         changeCallback: (_, value) =>
249                         {
250                             state = States.Idle;
251                             enabled = value;
252                         })
253                     .WithFlag(1, name: "CTRL0.mst",
254                         changeCallback: (_, value) =>
255                         {
256                             if(!value)
257                             {
258                                 this.Log(LogLevel.Warning, "CTRL0.mst has been set to false, but only Master mode is supported; ignoring");
259                             }
260                             state = States.Idle;
261                         })
262                     .WithTaggedFlag("CTRL0.gcen", 2)
263                     .WithTaggedFlag("CTRL0.irxm", 3)
264                     .WithTaggedFlag("CTRL0.ack", 4)
265                     .WithReservedBits(5, 1)
266                     .WithFlag(6, out var sclOutput, name: "CTRL0.sclo")
267                     .WithFlag(7, out var sdaOutput, name: "CTRL0.sdao")
268                     .WithFlag(8, FieldMode.Read, name: "CTRL0.scl",
269                         valueProviderCallback: _ => sclOutput.Value)
270                     .WithFlag(9, FieldMode.Read, name: "CTRL0.sda",
271                         valueProviderCallback: _ => sdaOutput.Value)
272                     .WithFlag(10, name: "CTRL0.swoe")
273                     .WithTaggedFlag("CTRL0.read", 11)
274                     .WithTaggedFlag("CTRL0.scl_strd", 12)
275                     .WithTaggedFlag("CTRL0.scl_ppm", 13)
276                     .WithReservedBits(14, 1)
277                     .WithTaggedFlag("CTRL0.hsmode", 15)
278                     .WithReservedBits(16, 16)
279                 },
280                 {(long)Registers.InterruptFlags0, new DoubleWordRegister(this)
281                     .WithFlag(0, out interruptDonePending, FieldMode.Read | FieldMode.WriteOneToClear, name: "INT_FL0.donei")
282                     .WithTaggedFlag("INT_FL0.irxmi", 1)
283                     .WithTaggedFlag("INT_FL0.gci", 2)
284                     .WithTaggedFlag("INT_FL0.ami", 3)
285                     .WithFlag(4, out interruptRxThresholdPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "INT_FL0.rxthi")
286                     .WithFlag(5, out interruptTxThresholdPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "INT_FL0.txthi")
287                     .WithFlag(6, out interruptStopPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "INT_FL0.stopi")
288                     .WithTaggedFlag("INT_FL0.adracki", 7)
289                     .WithTaggedFlag("INT_FL0.arberi", 8)
290                     .WithFlag(9, out interruptTimeoutPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "INT_FL0.toeri")
291                     .WithTaggedFlag("INT_FL0.adreri", 10)
292                     .WithTaggedFlag("INT_FL0.dateri", 11)
293                     .WithTaggedFlag("INT_FL0.dnreri", 12)
294                     .WithTaggedFlag("INT_FL0.strteri", 13)
295                     .WithTaggedFlag("INT_FL0.stoperi", 14)
296                     // We are using flag instead of tagged flag to hush down unnecessary logs
297                     .WithFlag(15, FieldMode.WriteOneToClear, name: "INT_FL0.txloi")
298                     .WithReservedBits(16, 16)
299                     .WithChangeCallback((_, __) => UpdateInterrupts())
300                 },
301                 {(long)Registers.InterruptEnable0, new DoubleWordRegister(this)
302                     .WithFlag(0, out interruptDoneEnabled, name: "INT_EN0.donei")
303                     .WithTaggedFlag("INT_EN0.irxmi", 1)
304                     .WithTaggedFlag("INT_EN0.gci", 2)
305                     .WithTaggedFlag("INT_EN0.ami", 3)
306                     .WithFlag(4, out interruptRxThresholdEnabled, name: "INT_EN0.rxthi")
307                     .WithFlag(5, out interruptTxThresholdEnabled, name: "INT_EN0.txthi")
308                     .WithFlag(6, out interruptStopEnabled, name: "INT_EN0.stopi")
309                     .WithTaggedFlag("INT_EN0.adracki", 7)
310                     .WithTaggedFlag("INT_EN0.arberi", 8)
311                     .WithFlag(9, out interruptTimeoutEnabled, name: "INT_EN0.toeri")
312                     .WithTaggedFlag("INT_EN0.adreri", 10)
313                     .WithTaggedFlag("INT_EN0.dateri", 11)
314                     .WithTaggedFlag("INT_EN0.dnreri", 12)
315                     .WithTaggedFlag("INT_EN0.strteri", 13)
316                     .WithTaggedFlag("INT_EN0.stoperi", 14)
317                     .WithTaggedFlag("INT_EN0.txloi", 15)
318                     .WithReservedBits(16, 16)
319                     .WithChangeCallback((_, __) => UpdateInterrupts())
320                 },
321                 {(long)Registers.ReceiveControl0, new DoubleWordRegister(this)
322                     .WithTaggedFlag("RX_CTRL0.dnr", 0)
323                     .WithReservedBits(1, 6)
324                     .WithFlag(7, FieldMode.Read | FieldMode.WriteOneToClear, name: "RX_CTRL0.rxfsh",
325                         writeCallback: (_, value) => { if(value) rxQueue.Clear(); })
326                     .WithValueField(8, 4, out rxThreshold, name: "RX_CTRL0.rxth")
327                     .WithReservedBits(12, 20)
328                 },
329                 {(long)Registers.ReceiveControl1, new DoubleWordRegister(this)
330                     .WithValueField(0, 8, name: "RX_CTRL1.rxcnt",
331                         valueProviderCallback: _ => (uint)bytesToReceive,
332                         writeCallback: (_, value) => bytesToReceive = (value == 0) ? 256 : (int)value)
333                     .WithValueField(8, 4, FieldMode.Read, name: "RX_CTRL1.rxfifo",
334                         valueProviderCallback: _ => (uint)rxQueue.Count)
335                     .WithReservedBits(12, 20)
336                 },
337                 {(long)Registers.TransmitControl0, new DoubleWordRegister(this)
338                     .WithFlag(0, name: "TX_CTRL0.txpreld")
339                     .WithReservedBits(1, 6)
340                     .WithFlag(7, FieldMode.Read | FieldMode.WriteOneToClear, name: "TX_CTRL0.txfsh",
341                         writeCallback: (_, value) => { if(value) txQueue.Clear(); })
342                     .WithValueField(8, 4, out txThreshold, name: "TX_CTRL0.txth")
343                     .WithReservedBits(12, 20)
344                 },
345                 {(long)Registers.FIFO, new DoubleWordRegister(this)
346                     .WithValueField(0, 8, name: "FIFO.data",
347                         valueProviderCallback: _ => HandleReadByte(),
348                         writeCallback: (_, value) => HandleWriteByte((byte)value))
349                     .WithReservedBits(8, 24)
350                 },
351                 {(long)Registers.MasterMode, new DoubleWordRegister(this)
352                     .WithFlag(0, FieldMode.Read | FieldMode.Set, name: "MSTR_MODE.start",
353                         valueProviderCallback: _ => state != States.Idle,
354                         writeCallback: (_, value) =>
355                         {
356                             if(value && state == States.Idle)
357                             {
358                                 state = States.Ready;
359                                 HandleTransaction();
360                             }
361                         })
362                     .WithFlag(1, FieldMode.Read | FieldMode.WriteOneToClear, name: "MSTR_MODE.restart",
363                         writeCallback: (_, value) =>
364                         {
365                             if(value)
366                             {
367                                 HandleTransaction();
368                                 state = States.Ready;
369                             }
370                         },
371                         valueProviderCallback: _ => false)
372                     .WithFlag(2, FieldMode.Read | FieldMode.WriteOneToClear, name: "MSTR_MODE.stop",
373                         writeCallback: (_, value) =>
374                         {
375                             if(!value)
376                             {
377                                 return;
378                             }
379 
380                             HandleTransaction();
381                             interruptStopPending.Value = true;
382                             interruptDonePending.Value = true;
383                             state = States.Idle;
384                             CurrentSlave?.FinishTransmission();
385                             UpdateInterrupts();
386                         })
387                     .WithReservedBits(3, 4)
388                     .WithFlag(7, out slaveExtendedAddress, name: "MSTR_MODE.sea")
389                     .WithReservedBits(8, 24)
390                 }
391             };
392             return registerMap;
393         }
394 
395         private II2CPeripheral CurrentSlave =>
396             TryGetByAddress((int)destinationAddress, out var peripheral) ? peripheral : null;
397 
398         private bool enabled;
399         private States state;
400         private uint destinationAddress;
401         private int bytesToReceive;
402 
403         private IFlagRegisterField slaveExtendedAddress;
404 
405         private IValueRegisterField rxThreshold;
406         private IValueRegisterField txThreshold;
407 
408         private IFlagRegisterField interruptTimeoutPending;
409         private IFlagRegisterField interruptRxThresholdPending;
410         private IFlagRegisterField interruptTxThresholdPending;
411         private IFlagRegisterField interruptStopPending;
412         private IFlagRegisterField interruptDonePending;
413 
414         private IFlagRegisterField interruptTimeoutEnabled;
415         private IFlagRegisterField interruptRxThresholdEnabled;
416         private IFlagRegisterField interruptTxThresholdEnabled;
417         private IFlagRegisterField interruptStopEnabled;
418         private IFlagRegisterField interruptDoneEnabled;
419 
420         private readonly Queue<byte> txQueue;
421         private readonly Queue<byte> rxQueue;
422 
423         private readonly DoubleWordRegisterCollection registers;
424 
425         private const byte DefaultReturnValue = 0x00;
426 
427         private enum States
428         {
429             Idle,
430             Ready,
431             WaitForAddress,
432             Reading,
433             ReadingFromBuffer,
434             Writing,
435         }
436 
437         private enum Registers
438         {
439             Control0 = 0x00,
440             Status = 0x04,
441             InterruptFlags0 = 0x08,
442             InterruptEnable0 = 0x0C,
443             InterruptFlags1 = 0x10,
444             InterruptEnable1 = 0x14,
445             FIFOLength = 0x18,
446             ReceiveControl0 = 0x1C,
447             ReceiveControl1 = 0x20,
448             TransmitControl0 = 0x24,
449             TransmitControl1 = 0x28,
450             FIFO = 0x2C,
451             MasterMode = 0x30,
452             ClockLowTime = 0x34,
453             ClockHighTime = 0x38,
454             Timeout = 0x40,
455             SlaveAddress = 0x44,
456             DMAEnable = 0x48,
457         }
458     }
459 }
460