1 //
2 // Copyright (c) 2010-2022 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 using Antmicro.Renode.Core;
8 using Antmicro.Renode.Core.Structure;
9 using Antmicro.Renode.Peripherals.I2C;
10 using Antmicro.Renode.Peripherals.Sensors;
11 using NUnit.Framework;
12 using I2C = Antmicro.Renode.Peripherals.I2C.OpenTitan_I2C;
13 
14 namespace Antmicro.Renode.PeripheralsTests
15 {
16     [TestFixture]
17     public class OpenTitan_I2C_Test
18     {
19         [SetUp]
Setup()20         public void Setup()
21         {
22             this.machine = new Machine();
23             this.sensor1 = new BMP180();
24             this.sensor2 = new PAC1934();
25             this.peripheral = new OpenTitan_I2C(machine);
26             machine.SystemBus.Register(peripheral, new Peripherals.Bus.BusRangeRegistration(new Range(0x1000, 0x1000)));
27             peripheral.Register(sensor1, new NumberRegistrationPoint<int>(Sensor1Address));
28             peripheral.Register(sensor2, new NumberRegistrationPoint<int>(Sensor2Address));
29         }
30 
31         [Test]
ShouldPerformSimpleTransaction()32         public void ShouldPerformSimpleTransaction()
33         {
34             EnableHost();
35             Assert.AreEqual(Sensor1Id, PerformReadFromSlave(Sensor1Address, Sensor1IdOffset), "Incorrect ID for sensor");
36         }
37 
38         [Test]
ShouldBeAbleToDoTwoConsecutiveTransmissions()39         public void ShouldBeAbleToDoTwoConsecutiveTransmissions()
40         {
41             EnableHost();
42             Assert.AreEqual(Sensor1Id, PerformReadFromSlave(Sensor1Address, Sensor1IdOffset), "Incorrect ID for sensor1");
43             Assert.AreEqual(Sensor2Id, PerformReadFromSlave(Sensor2Address, Sensor2IdOffset), "Incorrect ID for sensor2");
44         }
45 
46         [Test]
ShouldNotStartTransactionsUntilHostIsEnabled()47         public void ShouldNotStartTransactionsUntilHostIsEnabled()
48         {
49             Assert.AreEqual(0u, PerformReadFromSlave(Sensor1Address, Sensor1IdOffset), "Read some non-zero data");
50             // Assert that the fmt queue is not empty
51             Assert.AreEqual(0u, ReadFromRegister(I2C.Registers.Status) & 0x04, "Fmt queue empty");
52             // Assert that the rx queue is empty
53             Assert.AreEqual(RxQueueEmptyMask, ReadFromRegister(I2C.Registers.Status) & RxQueueEmptyMask, "Rx queue not empty");
54 
55             EnableHost();
56             // Assert that the rx queue is not empty
57             Assert.AreEqual(0u, ReadFromRegister(I2C.Registers.Status) & RxQueueEmptyMask, "Rx queue empty");
58             Assert.AreEqual(Sensor1Id, ReadFromRegister(I2C.Registers.ReadData), "Incorrect data read");
59         }
60 
61         [Test]
ShouldSetExceptionOnFmtWatermark()62         public void ShouldSetExceptionOnFmtWatermark()
63         {
64             // Enable the fmt overflow interrupt
65             WriteToRegister(I2C.Registers.InterruptEnable, 0x1);
66             // Set format fmt watermark level to 4
67             WriteToRegister(I2C.Registers.FifoControl, 1 << 5);
68             var command = new I2C.FormatIndicator(data: Sensor1Address, start: true);
69             for(var i = 0; i < 3; i++)
70             {
71                 EnqueueCommand(command);
72             }
73             // Interrupt not set yet
74             Assert.AreEqual(0u, ReadFromRegister(I2C.Registers.InterruptState), "Expected no interrupts at this point");
75             EnqueueCommand(command);
76             // Interrupt set
77             Assert.AreEqual(FmtWatermarkInterruptMask, ReadFromRegister(I2C.Registers.InterruptState) & FmtWatermarkInterruptMask, "Interrupt not set");
78             Assert.AreEqual(true, peripheral.FormatWatermarkIRQ.IsSet, "IRQ not set");
79         }
80 
81         [Test]
ShouldSetExceptionOnRxWatermark()82         public void ShouldSetExceptionOnRxWatermark()
83         {
84             // Enable the rx overflow interrupt
85             WriteToRegister(I2C.Registers.InterruptEnable, 0x2);
86 
87             EnableHost();
88             EnqueueCommand(new I2C.FormatIndicator(data: Sensor1Address, start: true));
89             // Select read address
90             EnqueueCommand(new I2C.FormatIndicator(data: Sensor1IdOffset));
91             // Interrupt not set yet
92             Assert.AreEqual(0u, ReadFromRegister(I2C.Registers.InterruptState) & RxWatermarkInterruptMask, "Expected no interrupt at this point");
93             EnqueueCommand(new I2C.FormatIndicator(data: 1, read: true, stop: true));
94             // Interrupt set
95             Assert.AreEqual(RxWatermarkInterruptMask, ReadFromRegister(I2C.Registers.InterruptState) & RxWatermarkInterruptMask, "Interrupt not set");
96             Assert.AreEqual(true, peripheral.RxWatermarkIRQ.IsSet, "IRQ not set");
97         }
98 
99         [Test]
ShouldSetExceptionOnOverflow()100         public void ShouldSetExceptionOnOverflow()
101         {
102             // Enable the fmt overflow interrupt
103             WriteToRegister(I2C.Registers.InterruptEnable, 0x1 << 2);
104             var command = new I2C.FormatIndicator(data: Sensor1Address, start: true);
105             for(var i = 0; i < 64; i++)
106             {
107                 EnqueueCommand(command);
108             }
109             // Interrupt not set yet
110             Assert.AreEqual(0u, ReadFromRegister(I2C.Registers.InterruptState) & FmtOverflowInterruptMask, "Expected no interrupt at this point");
111             EnqueueCommand(command);
112             // Interrupt set
113             Assert.AreEqual(FmtOverflowInterruptMask, ReadFromRegister(I2C.Registers.InterruptState) & FmtOverflowInterruptMask, "Interrupt not set");
114             Assert.AreEqual(true, peripheral.FormatOverflowIRQ.IsSet, "IRQ not set");
115         }
116 
117         [Test]
ShouldBeAbleToResetFmtFifo()118         public void ShouldBeAbleToResetFmtFifo()
119         {
120             EnqueueReadFromSlave(Sensor1Address, Sensor1IdOffset);
121 
122             // Assert that the format fifo is not empty
123             Assert.AreEqual(0u, ReadFromRegister(I2C.Registers.Status) & FmtFifoEmptyMask);
124             // Write one to the FMTRST
125             WriteToRegister(I2C.Registers.FifoControl, 0x01 << 1);
126             // Assert that the format fifo is not empty
127             Assert.AreEqual(FmtFifoEmptyMask, ReadFromRegister(I2C.Registers.Status) & FmtFifoEmptyMask);
128         }
129 
130         [Test]
ShouldBeAbleToResetRxFifo()131         public void ShouldBeAbleToResetRxFifo()
132         {
133             EnableHost();
134             EnqueueReadFromSlave(Sensor1Address, Sensor1IdOffset);
135 
136             // Assert that the rx queue is not empty
137             Assert.AreEqual(0u, ReadFromRegister(I2C.Registers.Status) & RxQueueEmptyMask, "RxQueue Empty");
138             // Clear Rx Fifo
139             WriteToRegister(I2C.Registers.FifoControl, 0x01 << 0);
140             Assert.AreEqual(RxQueueEmptyMask, ReadFromRegister(I2C.Registers.Status) & RxQueueEmptyMask);
141         }
142 
143         [Test]
ShouldNotAllowEnablingBothModesAtTheSameTime()144         public void ShouldNotAllowEnablingBothModesAtTheSameTime()
145         {
146             EnableTarget();
147             EnableHost();
148             Assert.AreEqual(1u, ReadFromRegister(I2C.Registers.Control));
149         }
150 
151         [Test]
ShouldNotAcceptCommandsWhenInTargetMode()152         public void ShouldNotAcceptCommandsWhenInTargetMode()
153         {
154             EnableTarget();
155             EnqueueCommand(new I2C.FormatIndicator(data: Sensor1Address, start: true));
156             Assert.AreEqual(FmtFifoEmptyMask, ReadFromRegister(I2C.Registers.Status) & FmtFifoEmptyMask);
157         }
158 
159         [Test]
ShouldQueueTransmitBytesWhenInTargetMode()160         public void ShouldQueueTransmitBytesWhenInTargetMode()
161         {
162             EnableTarget();
163             EnqueueTransmission(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 });
164             Assert.AreEqual(9u, GetTxFifoLevel());
165         }
166 
167         [Test]
ShouldNotQueueTransmissionWhenInHostMode()168         public void ShouldNotQueueTransmissionWhenInHostMode()
169         {
170             EnableHost();
171             EnqueueTransmission(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 });
172             Assert.AreEqual(0u, GetTxFifoLevel());
173         }
174 
175         [Test]
TargetShouldInterpretTransmissionCompleteAsStop()176         public void TargetShouldInterpretTransmissionCompleteAsStop()
177         {
178             EnableTarget();
179             peripheral.FinishTransmission();
180             Assert.AreEqual(new I2C.AcquireFormatIndicator(0x0, false, true),
181                             I2C.AcquireFormatIndicator.FromRegister(ReadFromRegister(I2C.Registers.AcquiredData)),
182                             "Expected stop flag");
183         }
184 
185         [Test]
TargetShouldCorrectlyAssignRWflag()186         public void TargetShouldCorrectlyAssignRWflag()
187         {
188             EnableTarget();
189             var someAddr = 0x7Fu;
190             var readBit = 0x1u;
191             peripheral.Write(new byte[] { (byte)((someAddr << 1) | readBit) });
192             peripheral.FinishTransmission();
193             readBit = 0x0u;
194             peripheral.Write(new byte[] { (byte)((someAddr << 1) | readBit) });
195             peripheral.FinishTransmission();
196             var acqFormat = I2C.AcquireFormatIndicator.FromRegister(ReadFromRegister(I2C.Registers.AcquiredData));
197 
198             Assert.AreEqual((someAddr << 1) | 1u, acqFormat.Data, "Expected correct data in the 1st paket");
199             Assert.AreEqual(true, acqFormat.ReadFlag, "Expected correct read flag in the 1st paket");
200             Assert.AreEqual(true, acqFormat.StartFlag, "Expected correct start flag in the 1st paket");
201             Assert.AreEqual(false, acqFormat.StopFlag, "Expected correct stop flag in the 1st paket");
202 
203             acqFormat = I2C.AcquireFormatIndicator.FromRegister(ReadFromRegister(I2C.Registers.AcquiredData));
204             Assert.AreEqual(true, acqFormat.StopFlag, "Expected correct stop flag in the 2nd ");
205 
206             acqFormat = I2C.AcquireFormatIndicator.FromRegister(ReadFromRegister(I2C.Registers.AcquiredData));
207             Assert.AreEqual((someAddr << 1) | 0u, acqFormat.Data, "Expected correct data in the 3rd paket");
208             Assert.AreEqual(false, acqFormat.ReadFlag, "Expected correct read flag in the 3rd paket");
209             Assert.AreEqual(true, acqFormat.StartFlag, "Expected correct start flag in the 3rd paket");
210             Assert.AreEqual(false, acqFormat.StopFlag, "Expected correct stop flag in the 3rd paket");
211 
212             acqFormat = I2C.AcquireFormatIndicator.FromRegister(ReadFromRegister(I2C.Registers.AcquiredData));
213             Assert.AreEqual(true, acqFormat.StopFlag, "Expected correct stop flag in the 4th packet");
214         }
215 
216         [Test]
TargetShouldProperlyHandleRead()217         public void TargetShouldProperlyHandleRead()
218         {
219             EnableTarget();
220             var data = new byte[] { 0x1, 0x2, 0xFF, 0x3, 0x4, 0xFE };
221             foreach(var b in data)
222             {
223                 WriteToRegister(I2C.Registers.TransmitData, b);
224             }
225             var readData = peripheral.Read(6);
226 
227             Assert.AreEqual(6, readData.Length, "Received data length mismatch");
228             Assert.AreEqual(data, readData, "Received data mismatch");
229         }
230 
231         [Test]
TargetShouldNotReturnMoreThanQueued()232         public void TargetShouldNotReturnMoreThanQueued()
233         {
234             EnableTarget();
235             WriteToRegister(I2C.Registers.TransmitData, 0x1);
236             Assert.AreEqual(1, peripheral.Read(10).Length);
237         }
238 
239         [Test]
ShouldCorrectlyShowStatusOfTheAcqFifo()240         public void ShouldCorrectlyShowStatusOfTheAcqFifo()
241         {
242             EnableTarget();
243             Assert.AreEqual(AcqFifoEmptyMask, ReadFromRegister(I2C.Registers.Status) & AcqFifoEmptyMask, "Fifo not empty after init");
244             for(var x = 0; x < 4; x++)
245             {
246                 peripheral.Write(new byte[] { 0x1 });
247             }
248             Assert.AreEqual(0, ReadFromRegister(I2C.Registers.Status) & AcqFifoEmptyMask, "Fifo empty after peripheral write?");
249             Assert.AreEqual(4, ((int)ReadFromRegister(I2C.Registers.FifoStatus) >> (int)AcqFifoLevelOffset) & FifoLevelMask, "Wrong fifo level returned");
250 
251             for(var x = 0; x < 60; x++)
252             {
253                 peripheral.Write(new byte[] { 0x1 });
254             }
255             Assert.AreEqual(AcqFifoFullMask, ReadFromRegister(I2C.Registers.Status) & AcqFifoFullMask, "Fifo not full after 64 writes?");
256             Assert.AreEqual(64, ReadFromRegister(I2C.Registers.FifoStatus) >> (int)AcqFifoLevelOffset, "Wrong fifo level returned");
257             WriteToRegister(I2C.Registers.FifoControl, AcqFifoResetMask);
258             Assert.AreEqual(AcqFifoEmptyMask, ReadFromRegister(I2C.Registers.Status) & AcqFifoEmptyMask, "Fifo not empty after reset");
259         }
260 
261         [Test]
ShouldCorrectlyShowStatusOfTheTxFifo()262         public void ShouldCorrectlyShowStatusOfTheTxFifo()
263         {
264             EnableTarget();
265             Assert.AreEqual(TxFifoEmptyMask, ReadFromRegister(I2C.Registers.Status) & TxFifoEmptyMask, "Fifo not empty after init");
266             for(var x = 0; x < 4; x++)
267             {
268                 WriteToRegister(I2C.Registers.TransmitData, 0x1);
269             }
270             Assert.AreEqual(0, ReadFromRegister(I2C.Registers.Status) & TxFifoEmptyMask, "Fifo empty?");
271             Assert.AreEqual(4, ReadFromRegister(I2C.Registers.FifoStatus) >> 8, "Wrong fifo level returned");
272 
273             for(var x = 0; x < 60; x++)
274             {
275                 WriteToRegister(I2C.Registers.TransmitData, 0x1);
276             }
277             Assert.AreEqual(TxFifoFullMask, ReadFromRegister(I2C.Registers.Status) & TxFifoFullMask, "Fifo not full after 64 writes?");
278             Assert.AreEqual(64, ReadFromRegister(I2C.Registers.FifoStatus) >> (int)TxFifoLevelOffset, "Wrong fifo level returned");
279             WriteToRegister(I2C.Registers.FifoControl, TxFifoResetMask);
280             Assert.AreEqual(TxFifoEmptyMask, ReadFromRegister(I2C.Registers.Status) & TxFifoEmptyMask, "Fifo not empty after reset");
281         }
282 
PerformReadFromSlave(byte address, byte offset)283         private byte PerformReadFromSlave(byte address, byte offset)
284         {
285             EnqueueReadFromSlave(address, offset);
286             return (byte)ReadFromRegister(I2C.Registers.ReadData);
287         }
288 
EnqueueReadFromSlave(byte address, byte offset)289         private void EnqueueReadFromSlave(byte address, byte offset)
290         {
291             // Select slave address
292             EnqueueCommand(new I2C.FormatIndicator(data: address, start: true));
293             // Select read address
294             EnqueueCommand(new I2C.FormatIndicator(data: offset));
295             // Read one byte from selected address
296             EnqueueCommand(new I2C.FormatIndicator(data: 1, read: true, stop: true));
297         }
298 
EnableHost()299         private void EnableHost()
300         {
301             WriteToRegister(I2C.Registers.Control, 0x1);
302         }
303 
EnableTarget()304         private void EnableTarget()
305         {
306             WriteToRegister(I2C.Registers.Control, 0x2);
307         }
308 
EnqueueTransmission(byte[] bytes)309         private void EnqueueTransmission(byte[] bytes)
310         {
311             foreach(var b in bytes)
312             {
313                 WriteToRegister(I2C.Registers.TransmitData, b);
314             }
315         }
316 
EnqueueCommand(I2C.FormatIndicator command)317         private void EnqueueCommand(I2C.FormatIndicator command)
318         {
319             WriteToRegister(I2C.Registers.FormatData, command.ToRegisterFormat());
320         }
321 
WriteToRegister(I2C.Registers register, uint value)322         private void WriteToRegister(I2C.Registers register, uint value)
323         {
324             peripheral.WriteDoubleWord((long)register, value);
325         }
326 
ReadFromRegister(I2C.Registers register)327         private uint ReadFromRegister(I2C.Registers register)
328         {
329             return peripheral.ReadDoubleWord((long)register);
330         }
331 
GetTxFifoLevel()332         private uint GetTxFifoLevel()
333         {
334             return (ReadFromRegister(I2C.Registers.FifoStatus) >> 8) & 0x7F;
335         }
336 
337         private IMachine machine;
338         private II2CPeripheral sensor1;
339         private II2CPeripheral sensor2;
340         private OpenTitan_I2C peripheral;
341 
342         private const byte Sensor1Address = 0x77;
343         private const byte Sensor1Id = 0x55;
344         private const byte Sensor1IdOffset = 0xD0;
345         private const byte Sensor2Address = 0x78;
346         private const byte Sensor2Id = 0x5B;
347         private const byte Sensor2IdOffset = 0xFD;
348 
349         private const uint FifoLevelMask = 0x7F;
350         private const uint AcqFifoLevelOffset = 24;
351         private const uint TxFifoLevelOffset = 8;
352 
353         private const uint AcqFifoResetMask = 0x80;
354         private const uint TxFifoResetMask = 0x100;
355 
356         private const uint AcqFifoEmptyMask = 0x200;
357         private const uint AcqFifoFullMask = 0x80;
358         private const uint FmtFifoEmptyMask = 0x04;
359         private const uint FmtOverflowInterruptMask = 0x4;
360         private const uint FmtWatermarkInterruptMask = 0x1;
361         private const uint RxQueueEmptyMask = 0x20;
362         private const uint RxWatermarkInterruptMask = 0x2;
363         private const uint TxFifoEmptyMask = 0x100;
364         private const uint TxFifoFullMask = 0x40;
365     }
366 }
367