1 //
2 // Copyright (c) 2010-2019 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 
8 using System;
9 using System.Linq;
10 using System.Collections.Generic;
11 
12 using Antmicro.Renode.Core;
13 using Antmicro.Renode.Utilities;
14 using Antmicro.Renode.Core.Structure;
15 using Antmicro.Renode.Core.Structure.Registers;
16 using Antmicro.Renode.Peripherals.Bus;
17 using Antmicro.Renode.Logging;
18 using Antmicro.Renode.Debugging;
19 
20 namespace Antmicro.Renode.Peripherals.I2C
21 {
22     public class LiteX_I2C : SimpleContainer<II2CPeripheral>, IDoubleWordPeripheral, IKnownSize
23     {
LiteX_I2C(IMachine machine)24         public LiteX_I2C(IMachine machine) : base(machine)
25         {
26             // 0 - clock, 1 - data, 2 - direction
27             i2cDecoder = new BitPatternDetector(new [] { true, true, true }, this);
28             i2cDecoder.RegisterPatternHandler((prev, curr) => !prev[ClockSignal] && curr[ClockSignal], name: "clockRising", action: HandleClockRising);
29             i2cDecoder.RegisterPatternHandler((prev, curr) => prev[ClockSignal] && !curr[ClockSignal], name: "clockFalling", action: HandleClockFalling);
30             i2cDecoder.RegisterPatternHandler((prev, curr) => prev[ClockSignal] && curr[ClockSignal] && prev[DataSignal] && !curr[DataSignal], name: "start", action: HandleStartCondition);
31             i2cDecoder.RegisterPatternHandler((prev, curr) => prev[ClockSignal] && curr[ClockSignal] && !prev[DataSignal] && curr[DataSignal], name: "stop", action: HandleStopCondition);
32 
33             var registers = new Dictionary<long, DoubleWordRegister>
34             {
35                 {(long)Registers.BitBang, new DoubleWordRegister(this, 0x5) // SCL && SDA are high by default
36                     .WithFlag(0, out var clockSignal, name: "SCL")
37                     .WithFlag(1, out var directionSignal, name: "OE")
38                     .WithFlag(2, out var dataSignal, name: "SDA")
39                     .WithWriteCallback((_, val) => i2cDecoder.AcceptState(new [] { clockSignal.Value, dataSignal.Value, directionSignal.Value }))
40                 },
41 
42                 {(long)Registers.Data, new DoubleWordRegister(this)
43                     .WithFlag(0, FieldMode.Read, valueProviderCallback: _ =>
44                     {
45                         if(directionSignal.Value)
46                         {
47                             this.Log(LogLevel.Warning, "Trying to read data, but the direction signal is set to OUTPUT");
48                             return true;
49                         }
50 
51                         if(bufferFromDevice.Count == 0)
52                         {
53                             if(state == State.Read)
54                             {
55                                 // try - maybe there is sth more waiting for us
56                                 ReadNextByteFromDevice();
57                             }
58 
59                             if(bufferFromDevice.Count == 0)
60                             {
61                                 this.Log(LogLevel.Warning, "There are no more output bits to read");
62                                 // SDA is high when no data
63                                 return true;
64                             }
65                         }
66 
67                         return bufferFromDevice.Peek();
68                     })
69                 },
70             };
71 
72             bufferFromDevice = new Queue<bool>();
73             bufferToDevice = new Stack<bool>();
74             bytesToDevice = new Queue<byte>();
75 
76             registersCollection = new DoubleWordRegisterCollection(this, registers);
77             Reset();
78         }
79 
Reset()80         public override void Reset()
81         {
82             registersCollection.Reset();
83             i2cDecoder.Reset();
84             ResetBuffers();
85 
86             state = State.Idle;
87             tickCounter = 0;
88             slave = null;
89             isRead = false;
90         }
91 
ReadDoubleWord(long offset)92         public uint ReadDoubleWord(long offset)
93         {
94             return registersCollection.Read(offset);
95         }
96 
WriteDoubleWord(long offset, uint value)97         public void WriteDoubleWord(long offset, uint value)
98         {
99             registersCollection.Write(offset, value);
100         }
101 
102         public long Size => 0x10;
103 
HandleStopCondition(bool[] signals)104         private void HandleStopCondition(bool[] signals)
105         {
106             if(slave != null && bytesToDevice.Count > 0)
107             {
108                 this.Log(LogLevel.Noisy, "Writing data to the device");
109                 slave.Write(bytesToDevice.DequeueAll());
110             }
111 
112             ResetBuffers();
113 
114             this.Log(LogLevel.Noisy, "Stop condition detected in state: {0}", state);
115             state = State.Idle;
116         }
117 
HandleStartCondition(bool[] signals)118         private void HandleStartCondition(bool[] signals)
119         {
120             this.Log(LogLevel.Noisy, "(repeated) Start condition detected in state: {0}", state);
121 
122             if(slave != null && bytesToDevice.Count > 0)
123             {
124                 this.Log(LogLevel.Noisy, "Writing data to the device");
125                 slave.Write(bytesToDevice.DequeueAll());
126             }
127 
128             ResetBuffers();
129             state = State.Address;
130         }
131 
HandleClockRising(bool[] signals)132         private void HandleClockRising(bool[] signals)
133         {
134             switch(state)
135             {
136                 case State.Address:
137                 {
138                     this.Log(LogLevel.Noisy, "Appending address bit #{0}: {1}", bufferToDevice.Count, signals[DataSignal]);
139                     bufferToDevice.Push(signals[DataSignal]);
140 
141                     if(bufferToDevice.Count == 7)
142                     {
143                         var address = (int)BitHelper.GetValueFromBitsArray(bufferToDevice.PopAll());
144 
145                         this.Log(LogLevel.Noisy, "Address decoded: 0x{0:X}", address);
146                         state = State.Operation;
147 
148                         if(!TryGetByAddress(address, out slave))
149                         {
150                             this.Log(LogLevel.Warning, "Trying to access a non-existing I2C device at address 0x{0:X}", address);
151                         }
152                     }
153                     break;
154                 }
155 
156                 case State.Operation:
157                 {
158                     isRead = signals[DataSignal];
159                     this.Log(LogLevel.Noisy, "Operation decoded: {0}", isRead ? "read" : "write");
160 
161                     state = State.AddressAck;
162                     break;
163                 }
164 
165                 case State.AddressAck:
166                 {
167                     // write ACK(false) or NACK(true)
168                     bufferFromDevice.Enqueue(slave == null);
169 
170                     if(slave == null)
171                     {
172                         // ignore the rest of transmission until the next (repeated) start condition
173                         state = State.Idle;
174                     }
175                     else if(isRead)
176                     {
177                         tickCounter = 0;
178                         state = State.Read;
179                     }
180                     else // it must be write
181                     {
182                         state = State.Write;
183                     }
184                     break;
185                 }
186 
187                 case State.Read:
188                 {
189                     tickCounter++;
190                     if(tickCounter == 8)
191                     {
192                         state = State.ReadAck;
193                     }
194                     break;
195                 }
196 
197                 case State.ReadAck:
198                 {
199                     tickCounter = 0;
200                     state = State.Read;
201                     break;
202                 }
203 
204                 case State.Write:
205                 {
206                     this.Log(LogLevel.Noisy, "Latching data bit #{0}: {1}", bufferToDevice.Count, signals[DataSignal]);
207                     bufferToDevice.Push(signals[DataSignal]);
208 
209                     if(bufferToDevice.Count == 8)
210                     {
211                         state = State.WriteAck;
212                     }
213                     break;
214                 }
215 
216                 case State.WriteAck:
217                 {
218                     DebugHelper.Assert(bufferToDevice.Count == 8);
219 
220                     var dataByte = (byte)BitHelper.GetValueFromBitsArray(bufferToDevice.PopAll());
221                     this.Log(LogLevel.Noisy, "Decoded data byte #{0}: 0x{1:X}", bytesToDevice.Count, dataByte);
222 
223                     bytesToDevice.Enqueue(dataByte);
224 
225                     // ACK
226                     this.Log(LogLevel.Noisy, "Enqueuing ACK");
227                     bufferFromDevice.Enqueue(false);
228 
229                     state = State.Write;
230                     break;
231                 }
232             }
233         }
234 
HandleClockFalling(bool[] signals)235         private void HandleClockFalling(bool[] signals)
236         {
237             if(!signals[DirectionSignal])
238             {
239                 bufferFromDevice.TryDequeue(out var unused);
240             }
241         }
242 
ResetBuffers()243         private void ResetBuffers()
244         {
245             bufferToDevice.Clear();
246             bufferFromDevice.Clear();
247             bytesToDevice.Clear();
248 
249             state = State.Idle;
250             slave = null;
251             isRead = false;
252         }
253 
ReadNextByteFromDevice()254         private void ReadNextByteFromDevice()
255         {
256             if(slave == null)
257             {
258                 this.Log(LogLevel.Warning, "Trying to read data from the device, but no slave is currently selected");
259                 return;
260             }
261 
262             this.Log(LogLevel.Noisy, "Reading byte from device");
263             foreach(var @byte in slave.Read())
264             {
265                 // bits in I2C are transmitted most-significant-bit first
266                 var bits = BitHelper.GetBits(@byte).Take(8).Reverse();
267                 foreach(var bit in bits)
268                 {
269                     bufferFromDevice.Enqueue(bit);
270                 }
271             }
272         }
273 
274         private readonly DoubleWordRegisterCollection registersCollection;
275         private readonly BitPatternDetector i2cDecoder;
276         private readonly Stack<bool> bufferToDevice;
277         private readonly Queue<bool> bufferFromDevice;
278         private readonly Queue<byte> bytesToDevice;
279 
280         private State state;
281         private int tickCounter;
282         private II2CPeripheral slave;
283         private bool isRead;
284 
285         private const int ClockSignal = 0;
286         private const int DataSignal = 1;
287         private const int DirectionSignal = 2;
288 
289         private enum Registers
290         {
291             BitBang = 0x0,
292             Data = 0x4
293         }
294 
295         private enum State
296         {
297             Idle,
298             Address,
299             AddressAck,
300             Operation,
301             Write,
302             WriteAck,
303             Read,
304             ReadAck
305         }
306     }
307 }
308