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 NUnit.Framework;
9 using Antmicro.Renode.Core;
10 using Antmicro.Renode.Core.Structure;
11 using Antmicro.Renode.Peripherals.I2C;
12 using System.Threading;
13 using System.Diagnostics;
14 using System.Collections.Generic;
15 using Antmicro.Renode.Peripherals.Mocks;
16 using Antmicro.Renode.Peripherals.Bus;
17 using Antmicro.Renode.Peripherals.Sensors;
18 
19 namespace Antmicro.Renode.PeripheralsTests
20 {
21     [TestFixture]
22     public class EFR32_I2CControllerTests
23     {
24         [SetUp]
SetupController()25         public void SetupController()
26         {
27             machine = new Machine();
28             controller = new EFR32_I2CController(machine);
29             readTestPeripheral = new DummyI2CSlave();
30             writeTestPeripheral = new EchoI2CDevice();
31 
32             controller.Reset();
33             readTestPeripheral.Reset();
34             // Enable the controller
35             // Also enable autoack, which will automatically acknowledge all incoming bytes
36             // so we don't have to do it manually.
37             controller.WriteDoubleWord(0x0, 0x1 | (1 << 2));
38             // Enable all interrupts
39             controller.WriteDoubleWord(0x40, 0x7FFFF);
40 
41             machine.SystemBus.Register(controller, new BusRangeRegistration(0x4A010000, 0x400));
42             controller.Register(readTestPeripheral, new NumberRegistrationPoint<int>(readTestPeripheralAddress));
43             controller.Register(writeTestPeripheral, new NumberRegistrationPoint<int>(writeTestPeripheralAddress));
44         }
45 
46         [Test]
WriteToSlaveUsingSingleByteTransfer()47         public void WriteToSlaveUsingSingleByteTransfer()
48         {
49             BeginTransmission(writeTestPeripheralAddress, TransmissionType.Write);
50             // Send some magic bytes to the device
51             foreach(var b in testMagicSequence)
52             {
53                 // Write the byte to send
54                 controller.WriteDoubleWord(0x2C, b);
55             }
56             EndTransmission();
57 
58             Assert.AreEqual(testMagicSequence, writeTestPeripheral.Read(testMagicSequence.Length));
59         }
60 
61         [Test, Timeout(2000)]
ReadFromSlaveUsingSingleByteTransfer()62         public void ReadFromSlaveUsingSingleByteTransfer()
63         {
64             // Write some data that will be mirrored back to the controller
65             EnqueueDummyBytes(testMagicSequence);
66 
67             BeginTransmission(readTestPeripheralAddress, TransmissionType.Read);
68             var bytes = ReadBytes(testMagicSequence.Length);
69             EndTransmission();
70 
71             Assert.AreEqual(testMagicSequence, bytes);
72         }
73 
74         [Test]
PeekSingleByte()75         public void PeekSingleByte()
76         {
77             // Write some data that will be mirrored back to the controller
78             EnqueueDummyBytes(testMagicSequence);
79 
80             BeginTransmission(readTestPeripheralAddress, TransmissionType.Read);
81             Assert.AreEqual(testMagicSequence[0], PeekRxByte());
82             EndTransmission();
83         }
84 
85         [Test]
WriteToSlaveUsingDoubleByteTransfer()86         public void WriteToSlaveUsingDoubleByteTransfer()
87         {
88             BeginTransmission(writeTestPeripheralAddress, TransmissionType.Write);
89             // Send some magic bytes to the device, two bytes at a time
90             for(var i = 0; i < testMagicSequence.Length; i += 2)
91             {
92                 uint value = (uint)testMagicSequence[i] | ((uint)testMagicSequence[i + 1] << 8);
93                 controller.WriteDoubleWord(0x30, value);
94             }
95             EndTransmission();
96 
97             Assert.AreEqual(testMagicSequence, writeTestPeripheral.Read(testMagicSequence.Length));
98         }
99 
100         [Test, Timeout(2000)]
ReadFromSlaveUsingDoubleByteTransfer()101         public void ReadFromSlaveUsingDoubleByteTransfer()
102         {
103             // Write some data that will be mirrored back to the controller
104             EnqueueDummyBytes(testMagicSequence);
105 
106             BeginTransmission(readTestPeripheralAddress, TransmissionType.Read);
107             var bytes = ReadBytesDouble(testMagicSequence.Length);
108             EndTransmission();
109 
110             Assert.AreEqual(testMagicSequence, bytes);
111         }
112 
113         [Test]
PeekDoubleByte()114         public void PeekDoubleByte()
115         {
116             // Write some data that will be mirrored back to the controller
117             EnqueueDummyBytes(testMagicSequence);
118 
119             BeginTransmission(readTestPeripheralAddress, TransmissionType.Read);
120 
121             var value = PeekRxDoubleByte();
122             Assert.AreEqual(testMagicSequence[0], (byte)value);
123             Assert.AreEqual(testMagicSequence[1], (byte)(value >> 8));
124 
125             EndTransmission();
126         }
127 
128         [Test]
InterruptReadUnderflow()129         public void InterruptReadUnderflow()
130         {
131             ReadRxByte(); // we only care about the IRQ flags afterwards
132 
133             AssertInterrupt(irqRxUnderflow);
134         }
135 
136         [Test]
InterruptMasterStop()137         public void InterruptMasterStop()
138         {
139             // Send stop command
140             controller.WriteDoubleWord(0x04, 0x2);
141 
142             AssertInterrupt(irqMasterStop);
143         }
144 
145         [Test]
InterruptStart()146         public void InterruptStart()
147         {
148             // Send start command
149             controller.WriteDoubleWord(0x4, 0x1);
150             // Start should have fired
151             AssertInterrupt(irqStart);
152         }
153 
154         [Test]
InterruptAck()155         public void InterruptAck()
156         {
157             BeginTransmission(readTestPeripheralAddress, TransmissionType.Read);
158             // As there is a peripheral connected at that address, this should be ACK'd
159             AssertInterrupt(irqAck);
160         }
161 
162         [Test]
InterruptNack()163         public void InterruptNack()
164         {
165             BeginTransmission(0x40, TransmissionType.Read);
166             // No peripheral at that address, this should be NACK'd
167             AssertInterrupt(irqNack);
168         }
169 
170         [Test]
InterruptBusHold()171         public void InterruptBusHold()
172         {
173             // On HW, the controller can lose bus arbitration at any time during the transmission
174             // For simulation purposes, the bus is "virtually" held by the controller for the entire duration of the transmission
175             BeginTransmission(readTestPeripheralAddress, TransmissionType.Read);
176             AssertInterrupt(irqBusHold);
177             EndTransmission();
178         }
179 
180         [Test]
InterruptTransmitBufferLevel()181         public void InterruptTransmitBufferLevel()
182         {
183             BeginTransmission(writeTestPeripheralAddress, TransmissionType.Write);
184             // Write test byte
185             controller.WriteDoubleWord(0x2C, 0x42);
186             EndTransmission();
187 
188             // TXBL should have fired
189             AssertInterrupt(irqTxBufferLevel);
190 
191             // Write a test byte to the transmit buffer
192             controller.WriteDoubleWord(0x2C, 0x42);
193             // This should have cleared the TXBL condition
194             AssertInterruptCleared(irqTxBufferLevel);
195         }
196 
197         [Test]
InterruptTransferCompleted()198         public void InterruptTransferCompleted()
199         {
200             BeginTransmission(writeTestPeripheralAddress, TransmissionType.Write);
201             // Write test byte
202             controller.WriteDoubleWord(0x2C, 0x42);
203             EndTransmission();
204 
205             // TXC should have fired
206             AssertInterrupt(irqTransferCompleted);
207         }
208 
209         [Test]
TestWithBMP180()210         public void TestWithBMP180()
211         {
212             var sensor = new BMP180();
213             sensor.Reset();
214             controller.Register(sensor, new NumberRegistrationPoint<int>(0x77));
215 
216             // Send the register address to start reading from (0xF6 - OutMSB)
217             BeginTransmission(0x77, TransmissionType.Write);
218             controller.WriteDoubleWord(0x2c, 0xF6);
219             EndTransmission();
220 
221             // Now, read from the sensor
222             BeginTransmission(0x77, TransmissionType.Read);
223             var bytes = ReadBytes(8);
224             EndTransmission();
225 
226             // The reset value for OutMSB is 0x80
227             Assert.NotZero(bytes.Length);
228             Assert.AreEqual(0x80, bytes[0]);
229         }
230 
BeginTransmission(int address, TransmissionType type)231         private void BeginTransmission(int address, TransmissionType type)
232         {
233             // Start transmission
234             controller.WriteDoubleWord(0x4, 0x1);
235 
236             // Write address of peripheral, with LSB cleared (direction: write)
237             var data = (address << 1) | (type == TransmissionType.Read ? 0x1 : 0x0);
238             controller.WriteDoubleWord(0x2C, (uint)data);
239         }
240 
EndTransmission()241         private void EndTransmission()
242         {
243             // Stop transmission
244             controller.WriteDoubleWord(0x4, 0x2);
245         }
246 
EnqueueDummyBytes(IEnumerable<byte> response)247         private void EnqueueDummyBytes(IEnumerable<byte> response)
248         {
249             foreach (var @byte in response)
250             {
251                 readTestPeripheral.EnqueueResponseByte(@byte);
252             }
253         }
254 
ReadStatus()255         private uint ReadStatus()
256         {
257             return controller.ReadDoubleWord(0xC);
258         }
259 
ReadInterruptStatus()260         private uint ReadInterruptStatus()
261         {
262             return controller.ReadDoubleWord(0x34);
263         }
264 
ReadRxByte()265         private byte ReadRxByte()
266         {
267             return (byte)controller.ReadDoubleWord(0x1C);
268         }
269 
ReadRxDoubleByte()270         private ushort ReadRxDoubleByte()
271         {
272             return (ushort)controller.ReadDoubleWord(0x20);
273         }
274 
PeekRxByte()275         private byte PeekRxByte()
276         {
277             return (byte)controller.ReadDoubleWord(0x24);
278         }
279 
PeekRxDoubleByte()280         private ushort PeekRxDoubleByte()
281         {
282             return (ushort)controller.ReadDoubleWord(0x28);
283         }
284 
ReadBytes(int count)285         private byte[] ReadBytes(int count)
286         {
287             var bytes = new Queue<byte>();
288 
289             // Read bytes while data is still available in the Rx buffer
290             var received = 0;
291             while(received < count && (ReadStatus() & 0x100) != 0)
292             {
293                 // Make sure there's actually a byte waiting in the Rx buffer
294                 Assert.AreEqual(irqMasterStop, ReadStatus() & irqMasterStop);
295                 bytes.Enqueue(ReadRxByte());
296                 received += 1;
297             }
298 
299             return bytes.ToArray();
300         }
301 
ReadBytesDouble(int count)302         private byte[] ReadBytesDouble(int count)
303         {
304             var bytes = new Queue<byte>();
305 
306             var received = 0;
307             // Read bytes while data is still available in the Rx buffer
308             while(received < count && (ReadStatus() & 0x100) != 0)
309             {
310                 // Make sure the rx buffer actually contains two bytes to read;
311                 // reading from it is otherwise undefined behavior
312                 Assert.AreEqual((1 << 9), ReadStatus() & (1 << 9));
313                 // Reading is done two bytes at a time (RXDOUBLE)
314                 var value = ReadRxDoubleByte();
315                 bytes.Enqueue((byte)value);
316                 bytes.Enqueue((byte)(value >> 8));
317                 received += 2;
318             }
319 
320             return bytes.ToArray();
321         }
322 
AssertInterrupt(uint irqMask)323         private void AssertInterrupt(uint irqMask)
324         {
325             Assert.AreEqual(irqMask, ReadInterruptStatus() & irqMask);
326         }
327 
AssertInterruptCleared(uint irqMask)328         private void AssertInterruptCleared(uint irqMask)
329         {
330             Assert.AreEqual(0, ReadInterruptStatus() & irqMask);
331         }
332 
333         private enum TransmissionType
334         {
335             Read,
336             Write
337         }
338 
339         private IMachine machine;
340         private EFR32_I2CController controller;
341         private EchoI2CDevice writeTestPeripheral;
342         private DummyI2CSlave readTestPeripheral;
343         private const int readTestPeripheralAddress = 0x10;
344         private const int writeTestPeripheralAddress = 0x20;
345         private const int irqRxUnderflow = 1 << 13;
346         private const int irqMasterStop = 1 << 8;
347         private const int irqStart = 1 << 0;
348         private const int irqAck = 1 << 6;
349         private const int irqNack = 1 << 7;
350         private const int irqBusHold = 1 << 11;
351         private const int irqTxBufferLevel = 1 << 4;
352         private const int irqTransferCompleted = 1 << 3;
353         // The length of the below sequence needs to be a multiple of 2 for double-width transfer tests to work
354         private static byte[] testMagicSequence = { 0x11, 0x22, 0x33, 0x44, 0xFF, 0xEE, 0xDD, 0xCC, 0x11, 0x22, 0x33, 0x44, 0xFF, 0xEE, 0xDD, 0xCC };
355     }
356 }