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 Antmicro.Renode.Core;
9 using Antmicro.Renode.Utilities;
10 using Antmicro.Renode.Extensions.Mocks;
11 using NUnit.Framework;
12 using SPI = Antmicro.Renode.Peripherals.SPI.OpenTitan_SpiDevice;
13 using Range = Antmicro.Renode.Core.Range;
14 
15 namespace Antmicro.Renode.PeripheralsTests
16 {
17     [TestFixture]
18     public class OpenTitan_SpiDeviceTest
19     {
20         [SetUp]
Setup()21         public void Setup()
22         {
23             machine = new Machine();
24             peripheral = new SPI(machine);
25             spiHost = new SpiHost(peripheral);
26             hostMachine = new HostMachine();
27             hostMachine.AddHostMachineElement(spiHost, "spiHostMock");
28             machine.SystemBus.Register(peripheral, new Peripherals.Bus.BusRangeRegistration(new Range(PeripheralRegistrationPoint, 0x2000)));
29         }
30 
31         [Test]
ShouldHaveCorrectFifoConfigurationAfterBoot()32         public void ShouldHaveCorrectFifoConfigurationAfterBoot()
33         {
34             SetGenericMode(clockEnable: false);
35             ReadFifoAddresses(out var readBase, out var readLimit, out var writeBase, out var writeLimit);
36             Assert.AreEqual(0, readBase, "Incorrect readBase");
37             Assert.AreEqual(2047, readLimit, "Incorrect readLimit");
38             Assert.AreEqual(2048, writeBase, "Incorrect writeBase");
39             Assert.AreEqual(4095, writeLimit, "Incorrect writeLimit");
40         }
41 
42         [Test]
ShouldReceiveDataToFifo()43         public void ShouldReceiveDataToFifo()
44         {
45             var fifoPointerResponse = ReadFromPeripheral(SPI.Registers.ReceiverFifoSramPointers);
46             Assert.AreEqual(0, fifoPointerResponse);
47             SetGenericMode(clockEnable: true);
48             spiHost.WriteBytes("deadbeef");
49 
50             SplitValIntoWords(ReadFromPeripheral(SPI.Registers.ReceiverFifoSramPointers), out _, out var rxFifoPointer);
51             Assert.AreEqual(4, rxFifoPointer, "Incorrect fifo pointer");
52 
53             var fifoLevelResponse = ReadFromPeripheral(SPI.Registers.AsyncFifoLevel);
54             Assert.AreEqual(4, fifoLevelResponse, "Incorrect fifo level");
55         }
56 
57         [Test]
ShouldBeAbleToTransmitData()58         public void ShouldBeAbleToTransmitData()
59         {
60             var hexstring = "deadbeef";
61             AppendPeripheralFifo(hexstring);
62 
63             SplitValIntoWords(ReadFromPeripheral(SPI.Registers.AsyncFifoLevel), out _, out var fifoLevel);
64             Assert.AreEqual(4, fifoLevel);
65             var output = spiHost.ReadBytes(4);
66             Assert.AreEqual(new byte[] { 0xde, 0xad, 0xbe, 0xef }, output);
67         }
68 
69         [Test]
ShouldBeAbleToResetFifos()70         public void ShouldBeAbleToResetFifos()
71         {
72             var hexstring = "deadbeef";
73 
74             spiHost.WriteBytes(hexstring);
75             AppendPeripheralFifo(hexstring);
76 
77             SplitValIntoWords(ReadFromPeripheral(SPI.Registers.AsyncFifoLevel), out var txPointer, out var rxPointer);
78             Assert.AreEqual(4, txPointer, "Incorrect tx fifo pointer");
79             Assert.AreEqual(4, rxPointer, "Incorrect rx fifo pointer");
80 
81             this.WriteToPeripheral(SPI.Registers.Control, 3 << 16); // reset tx and rx fifo
82             SplitValIntoWords(ReadFromPeripheral(SPI.Registers.AsyncFifoLevel), out txPointer, out rxPointer);
83             Assert.AreEqual(0, txPointer, "Failed to reset the txFifo");
84             Assert.AreEqual(0, rxPointer, "Failed to reset the rxFifo");
85         }
86 
87         [Test]
ShouldSetInterruptOnRxFifoFull()88         public void ShouldSetInterruptOnRxFifoFull()
89         {
90             // Enable this interrupt
91             WriteToPeripheral(SPI.Registers.InterruptEnable, RxFullInterruptMask);
92             for(var i = 0; i < (MaxFifoCapacity - 1); i++)
93             {
94                 spiHost.WriteBytes("FF");
95             }
96             Assert.False(peripheral.GenericRxFull.IsSet, "Interrupt should not be set yet");
97             spiHost.WriteBytes("FF");
98             Assert.True(peripheral.GenericRxFull.IsSet, "Interrupt not set when expected");
99         }
100 
101         [Test]
ShouldSetInterruptOnRxOverflow()102         public void ShouldSetInterruptOnRxOverflow()
103         {
104             // Enable this interrupt
105             WriteToPeripheral(SPI.Registers.InterruptEnable, RxFifoOverflowMask);
106             // by default the fifo can fit 2048 elements
107             for(var i = 0; i < MaxFifoCapacity; i++)
108             {
109                 spiHost.WriteBytes("FF");
110             }
111             Assert.False(peripheral.GenericRxOverflow.IsSet, "Overflow happend too fast");
112             spiHost.WriteBytes("FF");
113             Assert.True(peripheral.GenericRxOverflow.IsSet, "Interrupt not set when expected");
114         }
115 
116         [Test]
ShouldSetInterruptOnRxFifoWatermark()117         public void ShouldSetInterruptOnRxFifoWatermark()
118         {
119             // Enable this interrupt
120             WriteToPeripheral(SPI.Registers.InterruptEnable, RxWatermarkInterruptMask);
121             for(var i = 0; i < 0x80; i++)
122             {
123                 spiHost.WriteBytes("FF");
124             }
125             Assert.False(peripheral.GenericRxWatermark.IsSet, "Interrupt should not be set yet");
126             spiHost.WriteBytes("FF");
127             Assert.True(peripheral.GenericRxWatermark.IsSet, "Interrupt not set when expected");
128         }
129 
130         [Test]
ShouldSetInterruptOnTxFifoWatermark()131         public void ShouldSetInterruptOnTxFifoWatermark()
132         {
133             // Enable this interrupt
134             WriteToPeripheral(SPI.Registers.InterruptEnable, TxWatermarkInterruptMask);
135             // Set the rxWatermark
136             WriteToPeripheral(SPI.Registers.FifoLevel, 0x80 << 16);
137             WriteToPeripheral(SPI.Registers.TransmitterFifoSramPointers, 0x80 << 16);
138 
139             SplitValIntoWords(ReadFromPeripheral(SPI.Registers.AsyncFifoLevel), out _, out var txFifoLevel);
140             Assert.AreEqual(0x80, txFifoLevel, "Wrong fifo level");
141             Assert.False(peripheral.GenericTxWatermark.IsSet, "Interrupt should not be set yet");
142             peripheral.Transmit(0);
143             Assert.True(peripheral.GenericTxWatermark.IsSet, "Interrupt not set when expected");
144         }
145 
146         [Test]
ShouldSetInterruptOnTxUnderflow()147         public void ShouldSetInterruptOnTxUnderflow()
148         {
149             // Enable this interrupt
150             WriteToPeripheral(SPI.Registers.InterruptEnable, TxFifoUnderflowMask);
151             peripheral.Transmit(0);
152             Assert.True(peripheral.GenericTxUnderflow.IsSet, "Interrupt not set when expected");
153         }
154 
SetGenericMode(bool clockEnable)155         private void SetGenericMode(bool clockEnable)
156         {
157             var register = SPI.Registers.Control;
158             var valueToWrite = (0x0 << 4) | ((clockEnable ? 1 : 0) << 31);
159             WriteToPeripheral(register, (uint)valueToWrite);
160         }
161 
WriteToPeripheralTransmitFifo(byte[] bytes)162         private void WriteToPeripheralTransmitFifo(byte[] bytes)
163         {
164             // software is responsible for handling the pointers
165             ReadFifoAddresses(out _, out _, out var transmitWriteBase, out var transmitWriteLimit);
166             ReadFifoPointers(out _, out _, out _, out var transmitWritePointer);
167             var writeAddress = (ulong)SPI.Registers.Buffer + transmitWriteBase + transmitWritePointer;
168             foreach(var b in bytes)
169             {
170                 machine.SystemBus.WriteByte(PeripheralRegistrationPoint + writeAddress, b);
171                 writeAddress += 1;
172                 if(writeAddress == transmitWriteLimit)
173                 {
174                     writeAddress = transmitWriteBase;
175                 }
176             }
177             IncrementWritePointerBy(bytes.Length);
178         }
179 
IncrementWritePointerBy(int length)180         private void IncrementWritePointerBy(int length)
181         {
182             ReadFifoPointers(out _, out _, out var txReadPointer, out var txWritePointer);
183             txWritePointer += (uint)length;
184             WriteTxFifoPointers(txReadPointer, txWritePointer);
185         }
186 
AppendPeripheralFifo(string hexstring)187         private void AppendPeripheralFifo(string hexstring)
188         {
189             var dataToSend = Misc.HexStringToByteArray(hexstring);
190 
191             WriteToPeripheralTransmitFifo(dataToSend);
192         }
193 
WriteTxFifoPointers(uint txReadPointer, uint txWritePointer)194         private void WriteTxFifoPointers(uint txReadPointer, uint txWritePointer)
195         {
196             WriteToPeripheral(SPI.Registers.TransmitterFifoSramPointers, txReadPointer | (txWritePointer << 16));
197         }
198 
WriteToPeripheral(SPI.Registers register, uint value)199         private void WriteToPeripheral(SPI.Registers register, uint value)
200         {
201             machine.SystemBus.WriteDoubleWord(PeripheralRegistrationPoint + (ulong)register, value);
202         }
203 
WriteToPeripheral(SPI.Registers register, byte value)204         private void WriteToPeripheral(SPI.Registers register, byte value)
205         {
206             machine.SystemBus.WriteByte(PeripheralRegistrationPoint + (ulong)register, value);
207         }
208 
ReadFromPeripheral(SPI.Registers register)209         private uint ReadFromPeripheral(SPI.Registers register)
210         {
211             return machine.SystemBus.ReadDoubleWord(PeripheralRegistrationPoint + (ulong)register);
212         }
213 
ReadFifoAddresses(out uint readBase, out uint readLimit, out uint writeBase, out uint writeLimit)214         private void ReadFifoAddresses(out uint readBase, out uint readLimit, out uint writeBase, out uint writeLimit)
215         {
216             var rawReceiverVal = ReadFromPeripheral(SPI.Registers.ReceiverFifoSramAddresses);
217             var rawTransmitterVal = ReadFromPeripheral(SPI.Registers.TransmitterFifoSramAddresses);
218 
219             SplitValIntoWords(rawReceiverVal, out var baseRaw, out var limitRaw);
220             readBase = baseRaw * 4;
221             // weird conversion as this is the last doubleword addr
222             readLimit = (limitRaw + 4) * 4 - 1;
223 
224             SplitValIntoWords(rawTransmitterVal, out baseRaw, out limitRaw);
225             writeBase = baseRaw * 4;
226             // weird conversion as this is the last doubleword addr
227             writeLimit = (limitRaw + 4) * 4 - 1;
228         }
229 
ReadFifoPointers(out uint receiveReadPointer, out uint receiveWritePointer, out uint transmitReadPointer, out uint transmitWritePointer)230         private void ReadFifoPointers(out uint receiveReadPointer, out uint receiveWritePointer, out uint transmitReadPointer, out uint transmitWritePointer)
231         {
232             var receiverRawPointers = ReadFromPeripheral(SPI.Registers.ReceiverFifoSramPointers);
233             var transmitRawPointers = ReadFromPeripheral(SPI.Registers.TransmitterFifoSramPointers);
234 
235             SplitValIntoWords(receiverRawPointers, out receiveReadPointer, out receiveWritePointer);
236             SplitValIntoWords(transmitRawPointers, out transmitReadPointer, out transmitWritePointer);
237         }
238 
SplitValIntoWords(uint value, out uint firstWord, out uint secondWord)239         private void SplitValIntoWords(uint value, out uint firstWord, out uint secondWord)
240         {
241             firstWord = value & 0xFFFF;
242             secondWord = value >> 16;
243         }
244 
245         private IMachine machine;
246         private SPI peripheral;
247         private SpiHost spiHost;
248         private HostMachine hostMachine;
249 
250         private const uint PeripheralRegistrationPoint = 0x1000;
251         private const uint RxFullInterruptMask      = 1u << 0;
252         private const uint RxWatermarkInterruptMask = 1u << 1;
253         private const uint TxWatermarkInterruptMask = 1u << 2;
254         private const uint RxFifoOverflowMask       = 1u << 4;
255         private const uint TxFifoUnderflowMask      = 1u << 5;
256         private const uint MaxFifoCapacity = 2048;
257     }
258 }
259