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.Logging;
13 using Antmicro.Renode.Utilities;
14 using Antmicro.Renode.Core.Structure.Registers;
15 
16 namespace Antmicro.Renode.Peripherals.I2C
17 {
18     // This only implements I2C master operation
19     public abstract class EFR32_GenericI2CController : SimpleContainer<II2CPeripheral>
20     {
EFR32_GenericI2CController(IMachine machine)21         public EFR32_GenericI2CController(IMachine machine) : base(machine)
22         {
23             IRQ = new GPIO();
24             txBuffer = new Queue<byte>();
25             rxBuffer = new Queue<byte>();
26             interruptsManager = new InterruptManager<Interrupt>(this);
27         }
28 
Reset()29         public override void Reset()
30         {
31             currentAddress = 0;
32             isWrite = false;
33             waitingForAddressByte = false;
34             txBuffer.Clear();
35             rxBuffer.Clear();
36             interruptsManager.Reset();
37         }
38 
39         [IrqProvider]
40         public GPIO IRQ { get; }
41 
GenerateControlRegister()42         protected DoubleWordRegister GenerateControlRegister() => new DoubleWordRegister(this)
43             .WithFlag(0, out enableFlag, name: "EN")
44             .WithTaggedFlag("SLAVE", 1)
45             .WithFlag(2, out autoack, name: "AUTOACK")
46             .WithTaggedFlag("AUTOSE", 3)
47             .WithTaggedFlag("AUTOSN", 4)
48             .WithTaggedFlag("ARBDIS", 5)
49             .WithTaggedFlag("GCAMEN", 6)
50             .WithTaggedFlag("TXBIL", 7)
51             .WithTag("CLHR", 8, 2)
52             .WithReservedBits(10, 2)
53             .WithTag("BITO", 12, 2)
54             .WithReservedBits(14, 1)
55             .WithTaggedFlag("GIBITO", 15)
56             .WithTag("CLTO", 16, 3);
57 
GenerateCommandRegister()58         protected DoubleWordRegister GenerateCommandRegister() => new DoubleWordRegister(this)
59             .WithValueField(0, 8, FieldMode.Write, name: "COMMAND", writeCallback: (_, v) => HandleCommand((Command)v));
60 
GenerateStateRegister()61         protected DoubleWordRegister GenerateStateRegister() => new DoubleWordRegister(this)
62             .WithTaggedFlag("BUSY", 0)
63             .WithTaggedFlag("MASTER", 1)
64             .WithTaggedFlag("TRANSMITTER", 2)
65             .WithTaggedFlag("NACKED", 3)
66             .WithTaggedFlag("BUSHOLD", 4)
67             .WithTag("STATE", 5, 3)
68             .WithReservedBits(8, 24);
69 
GenerateStatusRegister()70         protected DoubleWordRegister GenerateStatusRegister() => new DoubleWordRegister(this)
71             .WithTaggedFlag("PSTART", 0)
72             .WithTaggedFlag("PSTOP", 1)
73             .WithTaggedFlag("PACK", 2)
74             .WithTaggedFlag("PNACK", 3)
75             .WithTaggedFlag("PCONT", 4)
76             .WithTaggedFlag("PABORT", 5)
77             .WithTaggedFlag("TXC", 6)
78             .WithFlag(7, mode: FieldMode.Read, valueProviderCallback: (_) => !txBuffer.Any(), name: "TXBL")
79             .WithFlag(8, mode: FieldMode.Read, valueProviderCallback: (_) => rxBuffer.Any(), name: "RXDATAV")
80             .WithFlag(9, mode: FieldMode.Read, valueProviderCallback: (_) => rxBuffer.Count >= maxRxBufferBytes, name: "RXFULL");
81 
GenerateClockDivisionRegister()82         protected DoubleWordRegister GenerateClockDivisionRegister() => new DoubleWordRegister(this)
83             .WithTag("DIV", 0, 9)
84             .WithReservedBits(9, 23);
85 
GenerateSlaveAddressRegister()86         protected DoubleWordRegister GenerateSlaveAddressRegister() => new DoubleWordRegister(this)
87             .WithReservedBits(0, 1)
88             .WithTag("ADDR", 1, 7)
89             .WithReservedBits(8, 24);
90 
GenerateSlaveAddressMaskRegister()91         protected DoubleWordRegister GenerateSlaveAddressMaskRegister() => new DoubleWordRegister(this)
92             .WithReservedBits(0, 1)
93             .WithTag("SADDRMASK", 1, 7)
94             .WithReservedBits(8, 24);
95 
GenerateReceiveBufferDataRegister()96         protected DoubleWordRegister GenerateReceiveBufferDataRegister() => new DoubleWordRegister(this)
97             .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => ReadRxByte(), name: "RXDATA");
98 
GenerateReceiveBufferDoubleDataRegister()99         protected DoubleWordRegister GenerateReceiveBufferDoubleDataRegister() => new DoubleWordRegister(this)
100             .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => ReadRxByte(), name: "RXDATA0")
101             .WithValueField(8, 8, FieldMode.Read, valueProviderCallback: _ => ReadRxByte(), name: "RXDATA1")
102             .WithReservedBits(16, 16);
103 
GenerateReceiveBufferDataPeekRegister()104         protected DoubleWordRegister GenerateReceiveBufferDataPeekRegister() => new DoubleWordRegister(this)
105             .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => PeekRxByte(0), name: "RXDATAP");
106 
GenerateReceiveBufferDoubleDataPeekRegister()107         protected DoubleWordRegister GenerateReceiveBufferDoubleDataPeekRegister() => new DoubleWordRegister(this)
108             .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => PeekRxByte(0), name: "RXDATAP0")
109             .WithValueField(8, 8, FieldMode.Read, valueProviderCallback: _ => PeekRxByte(1), name: "RXDATAP1")
110             .WithReservedBits(16, 16);
111 
GenerateTransmitBufferDataRegister()112         protected DoubleWordRegister GenerateTransmitBufferDataRegister() => new DoubleWordRegister(this)
113             .WithValueField(0, 8, FieldMode.Write, writeCallback: (_, v) => LoadTxData((byte)v), name: "TXDATA");
114 
GenerateTransmitBufferDoubleDataRegister()115         protected DoubleWordRegister GenerateTransmitBufferDoubleDataRegister() => new DoubleWordRegister(this)
116             .WithValueField(0, 8, FieldMode.Write, writeCallback: (_, v) => LoadTxData((byte)v), name: "TXDATA0")
117             .WithValueField(8, 8, FieldMode.Write, writeCallback: (_, v) => LoadTxData((byte)v), name: "TXDATA1")
118             .WithReservedBits(16, 16);
119 
GenerateInterruptFlagClearRegister()120         protected DoubleWordRegister GenerateInterruptFlagClearRegister() => interruptsManager.GetInterruptClearRegister<DoubleWordRegister>();
121 
GenerateInterruptFlagSetRegister()122         protected DoubleWordRegister GenerateInterruptFlagSetRegister() => interruptsManager.GetInterruptSetRegister<DoubleWordRegister>();
123 
GenerateInterruptEnableRegister()124         protected DoubleWordRegister GenerateInterruptEnableRegister() => interruptsManager.GetInterruptEnableRegister<DoubleWordRegister>();
125 
GenerateInterruptFlagRegister()126         protected DoubleWordRegister GenerateInterruptFlagRegister() => interruptsManager.GetRegister<DoubleWordRegister>(
127                 valueProviderCallback: (irq, __) => interruptsManager.IsSet(irq) && interruptsManager.IsEnabled(interrupt: irq),
128                 writeCallback: (irq, prevValue, newValue) =>
129                 {
130                     if (newValue)
131                     {
132                         interruptsManager.ClearInterrupt(irq);
133                     }
134                 }
135             );
136 
ReadRxByte()137         private byte ReadRxByte()
138         {
139             if(rxBuffer.TryDequeue(out var result))
140             {
141                 if(autoack.Value)
142                 {
143                     // When autoack is enabled, refill the Rx buffer to simulate an ACK being sent and
144                     // a new byte being received
145                     LoadRxData(1);
146                 }
147                 else
148                 {
149                     // RXDATAV is automatically cleared when reading the receive buffer
150                     // Check if any bytes remain and update the flag appropriately
151                     interruptsManager.SetInterrupt(Interrupt.ReceiveDataValid, rxBuffer.Any());
152                 }
153                 return result;
154             }
155 
156             // The buffer does not contain any data, the behavior is undefined.
157             // Raise the RXUF IRQ to signal the condition
158             interruptsManager.SetInterrupt(Interrupt.ReceiveBufferUnderflow);
159             return 0x0;
160         }
161 
PeekRxByte(int index)162         private byte PeekRxByte(int index)
163         {
164             if(index < rxBuffer.Count)
165             {
166                 return rxBuffer.ElementAt(index);
167             }
168 
169             // The buffer does not contain any data, the behavior is undefined.
170             // RXUF IRQ is _not_ raised in this case.
171             return 0x0;
172         }
173 
HandleCommand(Command command)174         private void HandleCommand(Command command)
175         {
176             foreach(var c in Enum.GetValues(typeof(Command)).Cast<Command>().Where(x => command.HasFlag(x)))
177             {
178                 switch(c)
179                 {
180                     case Command.SendStartCondition:
181                         OnStartCommand();
182                         break;
183 
184                     case Command.SendStopCondition:
185                         OnStopCommand();
186                         break;
187 
188                     case Command.SendAck:
189                         OnAckCommand();
190                         break;
191 
192                     case Command.SendNotAck:
193                         this.Log(LogLevel.Noisy, "Send NACK");
194                         interruptsManager.SetInterrupt(Interrupt.MasterStopCondition);
195                         break;
196 
197                     case Command.ClearTransmitBufferAndShiftRegister:
198                         this.Log(LogLevel.Noisy, "Cleared TX buffer");
199                         txBuffer.Clear();
200                         break;
201 
202                     default:
203                         this.Log(LogLevel.Warning, "Received unsupported command: {0}", c);
204                         break;
205                 }
206             }
207         }
208 
OnStartCommand()209         private void OnStartCommand()
210         {
211             // Note: This does not detect repeated starts after a read request
212             // We have to specifically handle repeated starts after a write as the data
213             // is written to the device only after a stop command is sent.
214             if(isWrite)
215             {
216                 this.Log(LogLevel.Noisy, "Repeated start command");
217                 interruptsManager.SetInterrupt(Interrupt.RepeatedStartCondition);
218                 OnStopCommand();
219             }
220 
221             interruptsManager.SetInterrupt(Interrupt.StartCondition);
222             switch(txBuffer.Count)
223             {
224                 case 0:
225                     // the first byte contains device address and R/W flag; we have to wait for it
226                     waitingForAddressByte = true;
227                     interruptsManager.SetInterrupt(Interrupt.BusHold);
228                     // TODO: here we should also set I2Cn_STATE to 0x57 according to p.442
229                     break;
230                 case 1:
231                     // there is a byte address waiting already in the buffer
232                     HandleAddressByte();
233                     break;
234                 default:
235                     // there is a byte address waiting already in the buffer, along with some data
236                     HandleAddressByte();
237                     // Clear TXBL, as some data is already present in the buffer
238                     interruptsManager.ClearInterrupt(interrupt: Interrupt.TransmitBufferLevel);
239                     HandleDataByte();
240                     break;
241             }
242         }
243 
OnStopCommand()244         private void OnStopCommand()
245         {
246             interruptsManager.SetInterrupt(Interrupt.MasterStopCondition);
247             if(!isWrite)
248             {
249                 return;
250             }
251 
252             WriteToSlave(targetPeripheral, txBuffer);
253             txBuffer.Clear();
254             interruptsManager.SetInterrupt(Interrupt.TransmitBufferLevel);
255             interruptsManager.SetInterrupt(Interrupt.TransferCompleted);
256             isWrite = false;
257         }
258 
OnAckCommand()259         private void OnAckCommand()
260         {
261             // Sending an ACK implies a read operation is in progress
262             if(isWrite)
263             {
264                 return;
265             }
266 
267             this.Log(LogLevel.Noisy, "ACK command");
268             // Fetch another byte from the target
269             LoadRxData(1);
270         }
271 
LoadTxData(byte value)272         private void LoadTxData(byte value)
273         {
274             // TXBL is cleared when new data is written to the transmit buffer
275             interruptsManager.ClearInterrupt(Interrupt.TransmitBufferLevel);
276 
277             txBuffer.Enqueue(value);
278             if(waitingForAddressByte)
279             {
280                 HandleAddressByte();
281             }
282             else
283             {
284                 HandleDataByte();
285             }
286         }
287 
HandleAddressByte()288         private void HandleAddressByte()
289         {
290             waitingForAddressByte = false;
291 
292             currentAddress = txBuffer.Dequeue();
293             isWrite = (currentAddress & 0x1) == 0;
294             currentAddress >>= 1;
295 
296             // Try getting the peripheral that the transmission is targeting
297             // If a peripheral at that address is not present, raise a NACK
298             if(!TryGetByAddress(currentAddress, out targetPeripheral))
299             {
300                 interruptsManager.SetInterrupt(Interrupt.NotAcknowledgeReceived);
301                 this.Log(LogLevel.Warning, "Trying to address non-existent I2C peripheral with address 0x{0:x}", currentAddress);
302                 return;
303             }
304 
305             // Device exists, ACK the address byte
306             interruptsManager.SetInterrupt(Interrupt.AcknowledgeReceived);
307 
308             // Immediately read data from the device if the transfer is a read
309             if(!isWrite)
310             {
311                 // If autoack is enabled, load two bytes instead to fill the Rx buffer
312                 LoadRxData(autoack.Value ? 2 : 1);
313             }
314         }
315 
HandleDataByte()316         private void HandleDataByte()
317         {
318             if(!isWrite)
319             {
320                 return;
321             }
322 
323             // The byte was already written to the Tx buffer; only need to update ACK state now
324             // If there is a device connected at the current address, then simulate an ACK.
325             // Otherwise, raise a NACK
326             if(targetPeripheral == null)
327             {
328                 interruptsManager.SetInterrupt(Interrupt.NotAcknowledgeReceived);
329             }
330             else
331             {
332                 interruptsManager.SetInterrupt(Interrupt.AcknowledgeReceived);
333             }
334         }
335 
LoadRxData(int count)336         private void LoadRxData(int count)
337         {
338             var spaceLeft = Math.Max(0, maxRxBufferBytes - rxBuffer.Count);
339             if(spaceLeft == 0)
340             {
341                 this.Log(LogLevel.Warning, "Requesting read but no space left in RX buffer");
342                 return;
343             }
344 
345             if(count > spaceLeft)
346             {
347                 this.Log(LogLevel.Warning, "Requesting read of {0} bytes, but have space only for {1} bytes", count, spaceLeft);
348             }
349 
350             var bytesToRead = Math.Min(spaceLeft, count);
351             ReadFromSlave(targetPeripheral, rxBuffer, bytesToRead);
352             interruptsManager.SetInterrupt(Interrupt.ReceiveDataValid, rxBuffer.Any());
353         }
354 
ReadFromSlave(II2CPeripheral slave, Queue<byte> buffer, int count)355         private void ReadFromSlave(II2CPeripheral slave, Queue<byte> buffer, int count)
356         {
357             if(slave == null)
358             {
359                 this.Log(LogLevel.Warning, "Trying to read from nonexisting slave with address \"{0}\"", currentAddress);
360                 return;
361             }
362 
363             var rxArray = slave.Read(count);
364             buffer.EnqueueRange(rxArray, count);
365 
366             this.Log(LogLevel.Noisy, "Devices returned {0} bytes of data.", rxArray.Length);
367         }
368 
WriteToSlave(II2CPeripheral slave, IEnumerable<byte> data)369         private void WriteToSlave(II2CPeripheral slave, IEnumerable<byte> data)
370         {
371             if(slave == null)
372             {
373                 this.Log(LogLevel.Warning, "Trying to write to nonexisting slave with address \"{0}\"", currentAddress);
374                 return;
375             }
376 
377             slave.Write(data.ToArray());
378         }
379 
380         private int currentAddress;
381         private bool isWrite;
382         private bool waitingForAddressByte;
383         private IFlagRegisterField enableFlag;
384         private IFlagRegisterField autoack;
385         private II2CPeripheral targetPeripheral;
386         private readonly Queue<byte> txBuffer;
387         private readonly Queue<byte> rxBuffer;
388         private readonly InterruptManager<Interrupt> interruptsManager;
389         private const int maxRxBufferBytes = 2;
390 
391         private enum Registers
392         {
393             Control = 0x00,
394             Command = 0x04,
395             State = 0x08,
396             Status = 0x0C,
397             ClockDivision = 0x10,
398             SlaveAddress = 0x14,
399             SlaveAddressMask = 0x18,
400             ReceiveBufferData = 0x1C,
401             ReceiveBufferDoubleData = 0x20,
402             ReceiveBufferDataPeek = 0x24,
403             ReceiveBufferDoubleDataPeek = 0x28,
404             TransmitBufferData = 0x2C,
405             TransmitBufferDoubleData = 0x30,
406             InterruptFlag = 0x34,
407             InterruptFlagSet = 0x38,
408             InterruptFlagClear = 0x3C,
409             InterruptEnable = 0x40,
410             IORoutingPinEnable = 0x44,
411             IORoutingLocation = 0x48
412         }
413 
414         [Flags]
415         private enum Command
416         {
417             SendStartCondition = 0x01,
418             SendStopCondition = 0x02,
419             SendAck = 0x04,
420             SendNotAck = 0x08,
421             ContinueTransmission = 0x10,
422             AbortTransmission = 0x20,
423             ClearTransmitBufferAndShiftRegister = 0x40,
424             ClearPendingCommands = 0x80
425         }
426 
427         private enum Interrupt
428         {
429             StartCondition = 0x00,
430             RepeatedStartCondition = 0x01,
431             Address = 0x02,
432             TransferCompleted = 0x03,
433             [NotSettable]
434             TransmitBufferLevel = 0x04,
435             [NotSettable]
436             ReceiveDataValid = 0x05,
437             AcknowledgeReceived = 0x06,
438             NotAcknowledgeReceived = 0x07,
439             MasterStopCondition = 0x08,
440             ArbitrationLost = 0x09,
441             BusError = 0x0A,
442             BusHold = 0x0B,
443             TransmitBufferOverflow = 0x0C,
444             ReceiveBufferUnderflow = 0x0D,
445             BusIdleTimeout = 0x0E,
446             ClockLowTimeout = 0x0F,
447             SlaveStopCondition = 0x10,
448             ReceiveBufferFull = 0x11,
449             ClockLowError = 0x12
450         }
451     }
452 }
453