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