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.Peripherals.Bus;
9 using Antmicro.Renode.Core.Structure.Registers;
10 using Antmicro.Renode.Core.Structure;
11 using Antmicro.Renode.Core;
12 using Antmicro.Renode.Logging;
13 using Antmicro.Renode.Utilities;
14 
15 namespace Antmicro.Renode.Peripherals.I2C
16 {
17     public class OpenCoresI2C : SimpleContainer<II2CPeripheral>, IBytePeripheral, IKnownSize
18     {
OpenCoresI2C(IMachine machine)19         public OpenCoresI2C(IMachine machine) : base(machine)
20         {
21             dataToSlave = new Queue<byte>();
22             dataFromSlave = new Queue<byte>();
23 
24             var commonRegistersMap = new Dictionary<long, ByteRegister>()
25             {
26                 {(long)Registers.Control, new ByteRegister(this)
27                     .WithFlag(7, out enabled)
28                     .WithTag("Interrupt enable", 6, 1)
29                     .WithReservedBits(0, 6)
30                 }
31             };
32 
33             var readRegistersMap = new Dictionary<long, ByteRegister>(commonRegistersMap)
34             {
35                 {(long)Registers.Receive, new ByteRegister(this)
36                     .WithValueField(0, 8, out receiveBuffer, FieldMode.Read)
37                 },
38 
39                 {(long)Registers.Status, new ByteRegister(this)
40                     .WithFlag(7, out receivedAckFromSlaveNegated, FieldMode.Read)
41                     .WithFlag(6, FieldMode.Read, valueProviderCallback: _ => transactionInProgress, name: "Busy")
42                     // We're using receivedAckFromSlaveNegated as we do not implement other ways of detecting arbitration loss
43                     .WithFlag(5, FieldMode.Read, valueProviderCallback: _ => receivedAckFromSlaveNegated.Value, name: "Arbitration lost")
44                     .WithReservedBits(2, 3)
45                     .WithFlag(1, FieldMode.Read, valueProviderCallback: _ => false, name: "Transfer in progress")
46                     .WithFlag(0, out interruptFlag, FieldMode.Read)
47                 }
48             };
49 
50             var writeRegistersMap = new Dictionary<long, ByteRegister>(commonRegistersMap)
51             {
52                 {(long)Registers.Transmit, new ByteRegister(this)
53                     .WithValueField(0, 8, out transmitBuffer, FieldMode.Write)
54                 },
55 
56                 {(long)Registers.Command, new ByteRegister(this)
57                     .WithFlag(7, out generateStartCondition, FieldMode.Write)
58                     .WithFlag(6, out generateStopCondition, FieldMode.Write)
59                     .WithFlag(5, out readFromSlave, FieldMode.Write)
60                     .WithFlag(4, out writeToSlave, FieldMode.Write)
61                     .WithTag("ACK", 3, 1)
62                     .WithReservedBits(1, 2)
63                     .WithFlag(0, FieldMode.Write, writeCallback: (_, __) => interruptFlag.Value = false)
64                     .WithWriteCallback((_, __) =>
65                     {
66                         if(!enabled.Value)
67                         {
68                             this.Log(LogLevel.Warning, "Writing to a disabled controller.");
69                             return;
70                         }
71 
72                         if(generateStartCondition.Value)
73                         {
74                             generateStartCondition.Value = false;
75                             if(transactionInProgress)
76                             {
77                                 // repeated start - finish previous transaction first
78                                 SendDataToSlave();
79                             }
80                             else
81                             {
82                                 transactionInProgress = true;
83                             }
84 
85                             dataFromSlave.Clear();
86 
87                             if(!TryResolveSelectedSlave(out selectedSlave))
88                             {
89                                 interruptFlag.Value = true;
90                                 transactionInProgress = false;
91                                 return;
92                             }
93                         }
94                         else if(writeToSlave.Value)
95                         {
96                             writeToSlave.Value = false;
97                             HandleWriteToSlaveCommand();
98                         }
99                         else if(readFromSlave.Value)
100                         {
101                             readFromSlave.Value = false;
102                             HandleReadFromSlaveCommand();
103                         }
104 
105                         if(generateStopCondition.Value)
106                         {
107                             generateStopCondition.Value = false;
108                             if(!transactionInProgress)
109                             {
110                                 this.Log(LogLevel.Warning, "Asked to generate STOP signal, but no START signal has been recently generated");
111                                 return;
112                             }
113 
114                             SendDataToSlave();
115                             selectedSlave = null;
116                             transactionInProgress = false;
117                         }
118                     })
119                 }
120             };
121 
122             readRegisters = new ByteRegisterCollection(this, readRegistersMap);
123             writeRegisters = new ByteRegisterCollection(this, writeRegistersMap);
124         }
125 
ReadByte(long offset)126         public byte ReadByte(long offset)
127         {
128             return readRegisters.Read(offset);
129         }
130 
WriteByte(long offset, byte value)131         public void WriteByte(long offset, byte value)
132         {
133             writeRegisters.Write(offset, value);
134         }
135 
Reset()136         public override void Reset()
137         {
138             readRegisters.Reset();
139             writeRegisters.Reset();
140             dataToSlave.Clear();
141             dataFromSlave.Clear();
142             selectedSlave = null;
143             transactionInProgress = false;
144         }
145 
146         public long Size => 0x1000;
147 
TryResolveSelectedSlave(out II2CPeripheral selectedSlave)148         private bool TryResolveSelectedSlave(out II2CPeripheral selectedSlave)
149         {
150             var slaveAddress = (byte)(transmitBuffer.Value >> 1);
151             if(!ChildCollection.TryGetValue(slaveAddress, out selectedSlave))
152             {
153                  this.Log(LogLevel.Warning, "Addressing unregistered slave: 0x{0:X}", slaveAddress);
154                  receivedAckFromSlaveNegated.Value = true;
155                  return false;
156             }
157 
158             receivedAckFromSlaveNegated.Value = false;
159             return true;
160         }
161 
HandleReadFromSlaveCommand()162         private void HandleReadFromSlaveCommand()
163         {
164             if(dataFromSlave.Count == 0)
165             {
166                 foreach(var b in selectedSlave.Read())
167                 {
168                     dataFromSlave.Enqueue(b);
169                 }
170 
171                 if(dataFromSlave.Count == 0)
172                 {
173                     this.Log(LogLevel.Warning, "Trying to read from slave, but no data is available");
174                     receiveBuffer.Value = 0;
175                     return;
176                 }
177             }
178 
179             receiveBuffer.Value = dataFromSlave.Dequeue();
180         }
181 
SendDataToSlave()182         private void SendDataToSlave()
183         {
184             if(dataToSlave.Count == 0 || selectedSlave == null)
185             {
186                 this.Log(LogLevel.Warning, "Trying to send data to slave, but either no data is available or the slave is not selected");
187                 return;
188             }
189 
190             selectedSlave.Write(dataToSlave.ToArray());
191             dataToSlave.Clear();
192         }
193 
HandleWriteToSlaveCommand()194         private void HandleWriteToSlaveCommand()
195         {
196             if(selectedSlave == null)
197             {
198                 this.Log(LogLevel.Warning, "Trying to write to not selected slave, ignoring");
199                 return;
200             }
201 
202             dataToSlave.Enqueue((byte)transmitBuffer.Value);
203             interruptFlag.Value = true;
204         }
205 
206         private bool transactionInProgress;
207         private II2CPeripheral selectedSlave;
208 
209         private readonly Queue<byte> dataToSlave;
210         private readonly Queue<byte> dataFromSlave;
211         private readonly IValueRegisterField receiveBuffer;
212         private readonly IValueRegisterField transmitBuffer;
213         private readonly IFlagRegisterField readFromSlave;
214         private readonly IFlagRegisterField writeToSlave;
215         private readonly IFlagRegisterField interruptFlag;
216         private readonly IFlagRegisterField enabled;
217         private readonly IFlagRegisterField receivedAckFromSlaveNegated;
218         private readonly IFlagRegisterField generateStartCondition;
219         private readonly IFlagRegisterField generateStopCondition;
220         private readonly ByteRegisterCollection readRegisters;
221         private readonly ByteRegisterCollection writeRegisters;
222 
223         private enum Registers
224         {
225             ClockPrescaleLow = 0x0,
226             ClockPrescaleHigh = 0x4,
227             Control = 0x8,
228             // no, it's not a bug  - Transmit is write-only, Receive read-only
229             Transmit = 0xC,
230             Receive = 0xC,
231             // no, it's not a bug  - Command is write-only, Status read-only
232             Command = 0x10,
233             Status = 0x10
234         }
235     }
236 }
237