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.Collections.Generic;
8 using Antmicro.Renode.Core;
9 using Antmicro.Renode.Core.Structure;
10 using Antmicro.Renode.Core.Structure.Registers;
11 using Antmicro.Renode.Logging;
12 using Antmicro.Renode.Peripherals.Bus;
13 using Antmicro.Renode.Utilities;
14 
15 namespace Antmicro.Renode.Peripherals.I2C
16 {
17     public sealed class STM32F7_I2C : SimpleContainer<II2CPeripheral>, II2CPeripheral, IDoubleWordPeripheral, IKnownSize
18     {
STM32F7_I2C(IMachine machine)19         public STM32F7_I2C(IMachine machine) : base(machine)
20         {
21             EventInterrupt = new GPIO();
22             ErrorInterrupt = new GPIO();
23             registers = CreateRegisters();
24             Reset();
25         }
26 
ReadDoubleWord(long offset)27         public uint ReadDoubleWord(long offset)
28         {
29             return registers.Read(offset);
30         }
31 
WriteDoubleWord(long offset, uint value)32         public void WriteDoubleWord(long offset, uint value)
33         {
34             registers.Write(offset, value);
35         }
36 
Reset()37         public override void Reset()
38         {
39             registers.Reset();
40             txData = new Queue<byte>();
41             rxData = new Queue<byte>();
42             currentSlaveAddress = 0;
43             transferOutgoing = false;
44             EventInterrupt.Unset();
45             ErrorInterrupt.Unset();
46             masterMode = false;
47         }
48 
Write(byte[] data)49         public void Write(byte[] data)
50         {
51             // RM0444 Rev 5, p.991/1390
52             // "0: Write transfer, slave enters receiver mode."
53             transferOutgoing = false;
54 
55             rxData.EnqueueRange(data);
56         }
57 
Read(int count = 1)58         public byte[] Read(int count = 1)
59         {
60             if(!addressMatched.Value)
61             {
62                 // Note 1:
63                 // RM0444 Rev 5, p.991/1390
64                 // "1: Read transfer, slave enters transmitter mode."
65                 // Note 2:
66                 // this is a workaround for the protocol not supporting start/stop bits
67                 transferOutgoing = (count > 0);
68                 bytesToTransfer.Value = (uint)count;
69                 addressMatched.Value = true;
70                 Update();
71             }
72 
73             if(txData.Count >= (int)bytesToTransfer.Value)
74             {
75                 // STOP condition
76                 stopDetection.Value = true;
77                 transmitInterruptStatus = false;
78                 addressMatched.Value = false;
79                 Update();
80             }
81             else
82             {
83                 // TODO: return partial results
84                 return new byte[0];
85             }
86 
87             var result = new byte[count];
88             for(var i = 0; i < count; i++)
89             {
90                 if(!txData.TryDequeue(out result[i]))
91                 {
92                     return new byte[0];
93                 }
94             }
95             return result;
96         }
97 
FinishTransmission()98         public void FinishTransmission()
99         {
100         }
101 
102         public long Size { get { return 0x400; } }
103 
104         public GPIO EventInterrupt { get; private set; }
105 
106         public GPIO ErrorInterrupt { get; private set; }
107 
108         public bool RxNotEmpty => rxData.Count > 0;
109 
110         public bool OwnAddress1Enabled => ownAddress1Enabled.Value;
111 
CreateRegisters()112         private DoubleWordRegisterCollection CreateRegisters()
113         {
114             var map = new Dictionary<long, DoubleWordRegister> { {
115                     (long)Registers.Control1, new DoubleWordRegister(this)
116                         .WithFlag(0, writeCallback: PeripheralEnabledWrite, name: "PE")
117                         .WithFlag(1, out transferInterruptEnabled, name: "TXIE")
118                         .WithFlag(2, out receiveInterruptEnabled, name: "RXIE")
119                         .WithFlag(3, out addressMatchedInterruptEnabled, name: "ADDRIE")
120                         .WithFlag(4, out nackReceivedInterruptEnabled, name: "NACKIE")
121                         .WithFlag(5, out stopDetectionInterruptEnabled, name: "STOPIE")
122                         .WithFlag(6, out transferCompleteInterruptEnabled, name: "TCIE")
123                         .WithTag("ERRIE", 7, 1)
124                         .WithTag("DNF", 8, 4)
125                         .WithTag("ANFOFF", 12, 1)
126                         .WithReservedBits(13, 1)
127                         .WithTag("TXDMAEN", 14, 1)
128                         .WithTag("RXDMAEN", 15, 1)
129                         .WithTag("SBC", 16, 1)
130                         .WithFlag(17, out noStretch, name: "NOSTRETCH")
131                         .WithTag("WUPEN", 18, 1)
132                         .WithTag("GCEN", 19, 1)
133                         .WithTag("SMBHEN", 20, 1)
134                         .WithTag("SMBDEN", 21, 1)
135                         .WithTag("ALERTEN", 22, 1)
136                         .WithTag("PECEN", 23, 1)
137                         .WithReservedBits(24, 8)
138                         .WithChangeCallback((_,__) => Update())
139                 }, {
140                     (long)Registers.Control2,
141                     new DoubleWordRegister(this)
142                         .WithValueField(0, 10, out slaveAddress, name: "SADD") //Changing this from a normal field to a callback requires a change in StartWrite
143                         .WithFlag(10, out isReadTransfer, name: "RD_WRN")
144                         .WithFlag(11, out use10BitAddressing, name: "ADD10")
145                         .WithTag("HEAD10R", 12, 1)
146                         .WithFlag(13, out start, name: "START")
147                         .WithFlag(14, out stop, name: "STOP")
148                         .WithTag("NACK", 15, 1)
149                         .WithValueField(16, 8, out bytesToTransfer, name: "NBYTES")
150                         .WithFlag(24, out reload, name: "RELOAD")
151                         .WithFlag(25, out autoEnd, name: "AUTOEND")
152                         .WithTag("PECBYTE", 26, 1)
153                         .WithReservedBits(27, 5)
154                         .WithWriteCallback((oldVal, newVal) =>
155                         {
156                             uint oldStart = (oldVal >> 13) & 1;
157                             uint oldBytesToTransfer = (oldVal >> 16) & 0xFF;
158 
159                             if(start.Value && stop.Value)
160                             {
161                                 this.Log(LogLevel.Warning, "Setting START and STOP at the same time, ignoring the transfer");
162                             }
163                             else if(start.Value)
164                             {
165                                 StartTransfer();
166                             }
167                             else if(stop.Value)
168                             {
169                                 StopTransfer();
170                             }
171 
172                             if(!start.Value)
173                             {
174                                 if(bytesToTransfer.Value > 0 && masterMode && transferCompleteReload.Value && currentSlave != null)
175                                 {
176                                     ExtendTransfer();
177                                 }
178                             }
179 
180                             if(oldStart == 1)
181                             {
182                                 if(oldBytesToTransfer != bytesToTransfer.Value)
183                                 {
184                                     this.Log(LogLevel.Error, "Changing NBYTES when START is set is not permitted");
185                                 }
186                             }
187 
188                             start.Value = false;
189                             stop.Value = false;
190                         })
191                         .WithChangeCallback((_,__) => Update())
192                 }, {
193                     (long)Registers.OwnAddress1, new DoubleWordRegister(this)
194                         .WithValueField(0, 10, out ownAddress1, name: "OA1")
195                         .WithFlag(10, out ownAddress1Mode, name: "OA1MODE")
196                         .WithReservedBits(11, 4)
197                         .WithFlag(15, out ownAddress1Enabled, name: "OA1EN")
198                         .WithReservedBits(16, 16)
199                         .WithWriteCallback((_, val) =>
200                             this.Log(LogLevel.Info, "Slave address 1: 0x{0:X}, mode: {1}, status: {2}", ownAddress1.Value, ownAddress1Mode.Value ? "10-bit" : "7-bit", ownAddress1Enabled.Value ? "enabled" : "disabled")
201                         )
202                 }, {
203                     (long)Registers.OwnAddress2, new DoubleWordRegister(this)
204                         .WithReservedBits(0, 1)
205                         .WithValueField(1, 7, out ownAddress2, name: "OA2")
206                         .WithValueField(8, 3, out ownAddress2Mask, name: "OA2MSK")
207                         .WithReservedBits(11, 4)
208                         .WithFlag(15, out ownAddress2Enabled, name: "OA2EN")
209                         .WithReservedBits(16, 16)
210                         .WithWriteCallback((_, val) =>
211                             this.Log(LogLevel.Info, "Slave address 2: 0x{0:X}, mask: 0x{1:X}, status: {2}", ownAddress2.Value, ownAddress2Mask.Value, ownAddress2Enabled.Value ? "enabled" : "disabled")
212                         )
213                 }, {
214                     (long)Registers.Timing, new DoubleWordRegister(this)
215                         .WithTag("SCLL", 0, 8)
216                         .WithTag("SCLH", 8, 8)
217                         .WithTag("SDADEL", 16, 4)
218                         .WithTag("SCLDEL", 20, 4)
219                         .WithReservedBits(24, 4)
220                         .WithTag("PRESC", 28, 4)
221                 }, {
222                     (long)Registers.InterruptAndStatus, new DoubleWordRegister(this, 1)
223                         .WithFlag(0,
224                             valueProviderCallback: _ => txData.Count == 0,
225                             writeCallback: (_, value)=>
226                             {
227                                 if(value)
228                                 {
229                                     txData.Clear();
230                                 }
231                             }, name: "TXE")
232                         .WithFlag(1,
233                             valueProviderCallback: _ => transmitInterruptStatus,
234                             writeCallback: (_, val) =>
235                             {
236                                 if(!noStretch.Value)
237                                 {
238                                     return;
239                                 }
240                                 transmitInterruptStatus = val && transferInterruptEnabled.Value;
241                             } , name: "TXIS")
242                         .WithFlag(2, FieldMode.Read, valueProviderCallback: _ => RxNotEmpty, name: "RXNE")
243                         .WithFlag(3, out addressMatched, FieldMode.Read, name: "ADDR")
244                         .WithTag("NACKF", 4, 1)
245                         .WithFlag(5, out stopDetection, FieldMode.Read, name: "STOPF")
246                         .WithFlag(6, out transferComplete, FieldMode.Read, name: "TC")
247                         .WithFlag(7, out transferCompleteReload, FieldMode.Read, name: "TCR")
248                         .WithTag("BERR", 8, 1)
249                         .WithTag("ARLO", 9, 1)
250                         .WithTag("OVR", 10, 1)
251                         .WithTag("PECERR", 11, 1)
252                         .WithTag("TIMEOUT", 12, 1)
253                         .WithTag("ALERT", 13, 1)
254                         .WithReservedBits(14, 1)
255                         .WithTag("BUSY", 15, 1)
256                         .WithFlag(16, FieldMode.Read, valueProviderCallback: _ => transferOutgoing, name: "DIR")
257                         .WithTag("ADDCODE", 17, 7)
258                         .WithReservedBits(24, 8)
259                         .WithChangeCallback((_,__) => Update())
260                 }, {
261                     (long)Registers.InterruptClear, new DoubleWordRegister(this, 0)
262                         .WithReservedBits(0, 3)
263                         .WithFlag(3, FieldMode.WriteOneToClear,
264                             writeCallback: (_, value) =>
265                             {
266                                 if(value)
267                                 {
268                                     transmitInterruptStatus = transferOutgoing & (txData.Count == 0);
269                                     addressMatched.Value = false;
270                                 }
271                             }, name: "ADDRCF")
272                         .WithTag("NACKCF", 4, 1)
273                         .WithFlag(5, FieldMode.WriteOneToClear,
274                             writeCallback: (_, value) =>
275                             {
276                                 if(value)
277                                 {
278                                     stopDetection.Value = false;
279                                 }
280                             }, name: "STOPCF")
281                         .WithReservedBits(6, 2)
282                         .WithTag("BERRCF", 8, 1)
283                         .WithTag("ARLOCF", 9, 1)
284                         .WithTag("OVRCF", 10, 1)
285                         .WithTag("PECCF", 11, 1)
286                         .WithTag("TIMOUTCF", 12, 1)
287                         .WithTag("ALERTCF", 13, 1)
288                         .WithReservedBits(14, 18)
289                         .WithChangeCallback((_,__) => Update())
290                 }, {
291                     (long)Registers.ReceiveData, new DoubleWordRegister(this, 0)
292                         .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: preVal => ReceiveDataRead((uint)preVal), name: "RXDATA")
293                         .WithReservedBits(9, 23)
294                 }, {
295                     (long)Registers.TransmitData, new DoubleWordRegister(this, 0)
296                         .WithValueField(0, 8, writeCallback: (prevVal, val) => HandleTransmitDataWrite((uint)prevVal, (uint)val), name: "TXDATA")
297                         .WithReservedBits(9, 23)
298                 }
299             };
300 
301             return new DoubleWordRegisterCollection(this, map);
302         }
303 
PeripheralEnabledWrite(bool oldValue, bool newValue)304         private void PeripheralEnabledWrite(bool oldValue, bool newValue)
305         {
306             if(newValue)
307             {
308                 return;
309             }
310             stopDetection.Value = false;
311             transferComplete.Value = false;
312             transferCompleteReload.Value = false;
313             transmitInterruptStatus = false;
314         }
315 
ExtendTransfer()316         private void ExtendTransfer()
317         {
318             //in case of reads we can fetch data from peripheral immediately, but in case of writes we have to wait until something is written to TXDATA
319             if(isReadTransfer.Value)
320             {
321                 var data = currentSlave.Read((int)bytesToTransfer.Value);
322                 foreach(var item in data)
323                 {
324                     rxData.Enqueue(item);
325                 }
326             }
327             transferCompleteReload.Value = false;
328             Update();
329         }
330 
StartTransfer()331         private void StartTransfer()
332         {
333             masterMode = true;
334             transferComplete.Value = false;
335 
336             currentSlave = null;
337 
338             rxData.Clear();
339             //This is kinda volatile. If we change slaveAddress setting to a callback action, it might not be set at this moment.
340             currentSlaveAddress = (int)(use10BitAddressing.Value ? slaveAddress.Value : ((slaveAddress.Value >> 1) & 0x7F));
341             if(!TryGetByAddress(currentSlaveAddress, out currentSlave))
342             {
343                 this.Log(LogLevel.Warning, "Unknown slave at address {0}.", currentSlaveAddress);
344                 return;
345             }
346 
347             if(isReadTransfer.Value)
348             {
349                 transmitInterruptStatus = false;
350                 var data = currentSlave.Read((int)bytesToTransfer.Value);
351                 foreach(var item in data)
352                 {
353                     rxData.Enqueue(item);
354                 }
355             }
356             else
357             {
358                 transmitInterruptStatus = true;
359             }
360             Update();
361         }
362 
StopTransfer()363         private void StopTransfer()
364         {
365             masterMode = false;
366             stopDetection.Value = true;
367             currentSlave?.FinishTransmission();
368             Update();
369         }
370 
ReceiveDataRead(uint oldValue)371         private uint ReceiveDataRead(uint oldValue)
372         {
373             if(rxData.Count > 0)
374             {
375                 var value = rxData.Dequeue();
376                 if(rxData.Count == 0)
377                 {
378                     SetTransferCompleteFlags(); //TC/TCR is set when NBYTES data have been transfered
379                 }
380                 return value;
381             }
382             this.Log(LogLevel.Warning, "Receive buffer underflow!");
383             return 0;
384         }
385 
HandleTransmitDataWrite(uint oldValue, uint newValue)386         private void HandleTransmitDataWrite(uint oldValue, uint newValue)
387         {
388             if(masterMode)
389             {
390                 MasterTransmitDataWrite(oldValue, newValue);
391             }
392             else
393             {
394                 SlaveTransmitDataWrite(oldValue, newValue);
395             }
396         }
397 
MasterTransmitDataWrite(uint oldValue, uint newValue)398         private void MasterTransmitDataWrite(uint oldValue, uint newValue)
399         {
400             if(currentSlave == null)
401             {
402                 this.Log(LogLevel.Warning, "Trying to send byte {0} to an unknown slave with address {1}.", newValue, currentSlaveAddress);
403                 return;
404             }
405             txData.Enqueue((byte)newValue);
406             if(txData.Count == (int)bytesToTransfer.Value)
407             {
408                 currentSlave.Write(txData.ToArray());
409                 txData.Clear();
410                 SetTransferCompleteFlags();
411             }
412         }
413 
SlaveTransmitDataWrite(uint oldValue, uint newValue)414         private void SlaveTransmitDataWrite(uint oldValue, uint newValue)
415         {
416             txData.Enqueue((byte)newValue);
417         }
418 
SetTransferCompleteFlags()419         private void SetTransferCompleteFlags()
420         {
421             if(!autoEnd.Value && !reload.Value)
422             {
423                 transferComplete.Value = true;
424             }
425             if(autoEnd.Value)
426             {
427                 currentSlave.FinishTransmission();
428                 stopDetection.Value = true;
429                 masterMode = false;
430             }
431             if(reload.Value)
432             {
433                 transferCompleteReload.Value = true;
434             }
435             else
436             {
437                 transmitInterruptStatus = false; //this is a guess based on a driver
438             }
439             Update();
440         }
441 
Update()442         private void Update()
443         {
444             var value = (transferCompleteInterruptEnabled.Value && (transferCompleteReload.Value || transferComplete.Value))
445                 || (transferInterruptEnabled.Value && transmitInterruptStatus)
446                 || (receiveInterruptEnabled.Value && isReadTransfer.Value && rxData.Count > 0) //RXNE is calculated dynamically
447                 || (stopDetectionInterruptEnabled.Value && stopDetection.Value)
448                 || (nackReceivedInterruptEnabled.Value && false) //TODO: implement NACKF
449                 || (addressMatchedInterruptEnabled.Value && addressMatched.Value);
450             EventInterrupt.Set(value);
451         }
452 
453         private IValueRegisterField bytesToTransfer;
454         private IValueRegisterField slaveAddress;
455         private IValueRegisterField ownAddress1;
456         private IValueRegisterField ownAddress2;
457         private IValueRegisterField ownAddress2Mask;
458         private IFlagRegisterField transferInterruptEnabled;
459         private IFlagRegisterField receiveInterruptEnabled;
460         private IFlagRegisterField addressMatchedInterruptEnabled;
461         private IFlagRegisterField nackReceivedInterruptEnabled;
462         private IFlagRegisterField stopDetectionInterruptEnabled;
463         private IFlagRegisterField transferCompleteInterruptEnabled;
464         private IFlagRegisterField isReadTransfer;
465         private IFlagRegisterField use10BitAddressing;
466         private IFlagRegisterField reload;
467         private IFlagRegisterField autoEnd;
468         private IFlagRegisterField noStretch;
469         private IFlagRegisterField ownAddress1Mode;
470         private IFlagRegisterField ownAddress1Enabled;
471         private IFlagRegisterField ownAddress2Enabled;
472         private IFlagRegisterField transferComplete;
473         private IFlagRegisterField transferCompleteReload;
474         private IFlagRegisterField stopDetection;
475         private IFlagRegisterField addressMatched;
476         private IFlagRegisterField start;
477         private IFlagRegisterField stop;
478 
479         private DoubleWordRegisterCollection registers;
480 
481         private II2CPeripheral currentSlave;
482         private Queue<byte> rxData;
483         private Queue<byte> txData;
484         private int currentSlaveAddress;
485         private bool transferOutgoing;
486         private bool transmitInterruptStatus;
487         private bool masterMode;
488 
489         private enum Registers
490         {
491             Control1 = 0x00,
492             Control2 = 0x04,
493             OwnAddress1 = 0x08,
494             OwnAddress2 = 0x0C,
495             Timing = 0x10,
496             Timeout = 0x14,
497             InterruptAndStatus = 0x18,
498             InterruptClear = 0x1C,
499             PacketErrorChecking = 0x20,
500             ReceiveData = 0x24,
501             TransmitData = 0x28
502         }
503     }
504 }
505