// // Copyright (c) 2010-2018 Antmicro // // This file is licensed under the MIT License. // Full license text is available in 'licenses/MIT.txt'. // using System; using NUnit.Framework; using Antmicro.Renode.Utilities; using System.IO; using Antmicro.Renode.Peripherals.Bus; using Machine = Antmicro.Renode.Core.Machine; using Antmicro.Renode.Exceptions; namespace Antmicro.Renode.UnitTests { [TestFixture] public class SVDParserTests { [OneTimeSetUp] public void Init() { currentMachine = new Machine(); } #region Format tests [Test] public void ShouldThrowOnNonexistingFile() { Assert.Throws(() => { currentMachine = new Machine(); device = new SVDParser(Path.Combine("invalid", "path.svd"), currentMachine.SystemBus); }); } [Test] public void ShouldThrowOnEmptyFile() { Assert.Throws(() => { SetUpDeviceWithString(""); }); } [Test] public void ShouldThrowOnInvalidXml() { Assert.Throws(() => { SetUpDeviceWithString("Lorem ipsum..."); }); } [Test] public void ShouldThrowOnInvalidSvd() { Assert.Throws(() => { SetUpDeviceWithString(@" "); }); } [Test] public void ShouldThrowOnDeviceWithNoMandatoryFields() { Assert.Throws(() => { SetUpDeviceWithString(@" "); }); } [Test] public void ShouldThrowOnDeviceWithNoPeripheralsTag() { Assert.Throws(() => { SetUpDeviceWithInfix(""); }); } [Test] public void ShouldThrowOnDeviceWithNotEveryMandatoryField() { // Both tags and are mandatory. string[] mandatories = { "descripion", "name" }; foreach(var item in mandatories) { Assert.Throws(() => { SetUpDeviceWithString($@" <{item}>value " ); }); } } [Test] public void ShouldThrowOnCpuWithoutEndianness() { // If cpu is defined the tag is mandatory. Assert.Throws(() => { SetUpDeviceWithString($@" Test description Test name " ); }); } [Test] public void ShouldThrowOnPeripheralWithNotEveryMandatoryField([Values("name", "baseAddress")] string tag) { // Both tags and are mandatory. // 0x1000 is used as a field value so we do not have to care about field types in this test. Assert.Throws(() => { SetUpDeviceWithInfix($@" <{tag}>0x1000 "); }); } [Test] public void ShouldThrowOnClusterWithoutMandatoryFields() { // Tag is mandatory. Assert.Throws(() => { SetUpDeviceWithInfix(@" Peripheral1 0x1000 REG1 0x0 0 "); }); } [Test] public void ShouldThrowOnRegisterWithNotEveryMandatoryField([Values("name", "addressOffset")] string tag) { // Both tags and are mandatory. // 0x1000 is used as a field value so we do not have to care about field types in this test. Assert.Throws(() => { SetUpDeviceWithInfix($@" Peripheral1 0x1000 0 <{tag}>0x1000 "); }); } [Test] public void ShouldThrowOnDeviceWithInvalidSize() { Assert.Throws(() => { SetUpDeviceWithString(@" Test description Test name invalidValue " ); }); } [Test] public void ShouldThrowOnDeviceWithInvalidResetValue() { Assert.Throws(() => { SetUpDeviceWithString(@" Test description Test name invalidValue " ); }); } [Test] public void ShouldThrowOnDeviceWithInvalidAccess() { Assert.Throws(() => { SetUpDeviceWithString(@" Test description Test name invalidValue " ); }); } [Test] public void ShouldThrowOnCpuWithInvalidEndianness() { Assert.Throws(() => { SetUpDeviceWithString($@" Test description Test name invalidValue " ); }); } [Test] public void ShouldThrowOnRegisterWithoutDeterminedSizeAndResetValue() { Assert.Throws(() => { SetUpDeviceWithString($@" Test description Test name read-write name 0 REG1 0 " ); }); } #endregion Format tests #region Read tests [Test] public void ShouldReadValueFromRegister() { var variableValue = 0xDEADBEEF; SetUpDeviceWithInfix($@" Peripheral1 0x1000 REG1 0x0 {variableValue} "); Assert.IsTrue(device.TryReadAccess(0x1000, out var result, SysbusAccessWidth.DoubleWord)); Assert.AreEqual(result, variableValue); } [Test] public void ShouldReadValueFromRegisterInBigEndian() { var variableValue = 0xDEADBEEF; byte[] bytes = BitConverter.GetBytes(variableValue); SetUpDeviceWithInfix($@" Peripheral1 0x1000 REG1 0x0 {variableValue} ", false ); byte[] newBytes = { bytes[3], bytes[2], bytes[1], bytes[0] }; var expectedValue = BitConverter.ToUInt32(newBytes, 0); Assert.IsTrue(device.TryReadAccess(0x1000, out var result, SysbusAccessWidth.DoubleWord)); Assert.AreEqual(result, expectedValue); } [Test] public void ShouldHandleDifferentAccessPermissions([Values("write-only", "read-only", "read-write", "read-writeOnce", "writeOnce")] string access) { SetUpDeviceWithInfix($@" Peripheral1 0x1000 REG1 0x0 0x01234567 {access} "); var returnValue = device.TryReadAccess(0x1000, out var result, SysbusAccessWidth.DoubleWord); if(access == "write-only" || access == "writeOnce") { Assert.AreEqual(result, 0); Assert.IsTrue(returnValue); } else { Assert.AreEqual(result, 0x01234567); Assert.IsTrue(returnValue); } } [Test] public void ShouldReadFromRegistersOfDifferentSizes() { var variableValue = 0xDEADBEEF; var maxSize = 32; for(var i = 1; i < maxSize; i++) { SetUpDeviceWithInfix($@" Peripheral1 0x1000 REG1 0x0 {i} {variableValue} read-write "); var mask = (uint)((1ul << i) - 1); var expectedValue = variableValue & mask; Assert.IsTrue(device.TryReadAccess(0x1000, out var result, SysbusAccessWidth.DoubleWord)); Assert.AreEqual(result, expectedValue); } } [Test] public void ShouldReadFromUnalignedOffsetInOnePeripheral() { byte[] bytes = { 11, 22, 33, 44, 55, 66, 77, 88 }; SetUpDeviceWithInfix($@" read-write Peripheral1 0x1000 REG1 0x0 {BitConverter.ToUInt32(bytes, 0)} 32 REG2 0x4 {bytes[4]} 8 REG3 0x5 {bytes[5]} 8 REG4 0x6 {BitConverter.ToInt16(bytes, 6)} 16 "); for(var i = -3; i < 8; i++) { var readingAddress = (ulong)(0x1000 + i); var expectedBytes = new byte[4]; for(var j = 0u; j < 4; j++) { if(readingAddress + j >= 0x1000 && readingAddress + j < 0x1008) { expectedBytes[j] = bytes[readingAddress + j - 0x1000]; } else { expectedBytes[j] = 0; } } var expectedValue = BitConverter.ToUInt32(expectedBytes, 0); Assert.IsTrue(device.TryReadAccess(readingAddress, out var result, SysbusAccessWidth.DoubleWord)); Assert.AreEqual(result, expectedValue); } } [Test] public void ShouldReadFromUnalignedOffsetInManyPeripherals() { byte[] bytes = { 11, 22, 33, 44, 55, 66, 77, 88 }; SetUpDeviceWithInfix($@" read-write Peripheral1 0x1000 REG1 0x0 {BitConverter.ToUInt32(bytes, 0)} 32 read-write Peripheral2 0x1004 REG2 0 {bytes[4]} 8 REG3 1 {bytes[5]} 8 read-write Peripheral3 0x1006 REG4 0 {BitConverter.ToInt16(bytes, 6)} 16 "); for(var i = -3; i < 8; i++) { var readingAddress = (ulong)(0x1000 + i); var expectedBytes = new byte[4]; for(var j = 0u; j < 4; j++) { if(readingAddress + j >= 0x1000 && readingAddress + j < 0x1008) { expectedBytes[j] = bytes[readingAddress + j - 0x1000]; } else { expectedBytes[j] = 0; } } var expectedValue = BitConverter.ToUInt32(expectedBytes, 0); Assert.IsTrue(device.TryReadAccess(readingAddress, out var result, SysbusAccessWidth.DoubleWord)); Assert.AreEqual(result, expectedValue); } } [Test] public void ShouldReadFromUnalignedOffsetInOnePeripheralInBigEndian() { byte[] bytes = { 11, 22, 33, 44, 55, 66, 77, 88 }; SetUpDeviceWithInfix($@" read-write Peripheral1 0x1000 REG1 0x0 {BitConverter.ToUInt32(bytes, 0)} 32 REG2 0x4 {bytes[4]} 8 REG3 0x5 {bytes[5]} 8 REG4 0x6 {BitConverter.ToInt16(bytes, 6)} 16 ", false ); var readingAddress = (ulong)(0x1000 - 3); for(var i = -3; i < 8; i++) { var expectedBytes = new byte[4]; for(var j = 0u; j < 4; j++) { var tmpAddres = readingAddress + j; if(tmpAddres >= 0x1000 && tmpAddres < 0x1008) { if(tmpAddres <= 0x1003) { var offset = tmpAddres - 0x1000; var bigEndianAddress = 0x1003 - offset; expectedBytes[j] = bytes[bigEndianAddress - 0x1000]; } else if(tmpAddres == 0x1004) { expectedBytes[j] = bytes[tmpAddres - 0x1000]; } else if(tmpAddres == 0x1005) { expectedBytes[j] = bytes[tmpAddres - 0x1000]; } else { var offset = tmpAddres - 0x1006; var bigEndianAddress = 0x1007 - offset; expectedBytes[j] = bytes[bigEndianAddress - 0x1000]; } } else { expectedBytes[j] = 0; } } var expectedValue = BitConverter.ToUInt32(expectedBytes, 0); Assert.IsTrue(device.TryReadAccess(readingAddress, out var result, SysbusAccessWidth.DoubleWord)); Assert.AreEqual(result, expectedValue); readingAddress++; } } [Test] public void ShouldReadFromUnalignedOffsetInOnePeripheralWithWriteOnlyRegister() { byte[] bytes = { 11, 22, 33, 44, 55, 66, 77, 88 }; SetUpDeviceWithInfix($@" read-write Peripheral1 0x1000 REG1 0x0 {BitConverter.ToUInt32(bytes, 0)} 32 REG2 0x4 {bytes[4]} write-only 8 REG3 0x5 {bytes[5]} write-only 8 REG4 0x6 {BitConverter.ToInt16(bytes, 6)} 16 ", false ); var readingAddress = (ulong)(0x1000 - 3); for(var i = -3; i < 8; i++) { var expectedBytes = new byte[4]; for(var j = 0u; j < 4; j++) { var tmpAddres = readingAddress + j; if(tmpAddres >= 0x1000 && tmpAddres < 0x1008) { if(tmpAddres == 0x1004 || tmpAddres == 0x1005) { expectedBytes[j] = 0; } else if(tmpAddres <= 0x1003) { var offset = tmpAddres - 0x1000; var bigEndianAddress = 0x1003 - offset; expectedBytes[j] = bytes[bigEndianAddress - 0x1000]; } else if(tmpAddres == 0x1004) { expectedBytes[j] = bytes[tmpAddres - 0x1000]; } else if(tmpAddres == 0x1005) { expectedBytes[j] = bytes[tmpAddres - 0x1000]; } else { var offset = tmpAddres - 0x1006; var bigEndianAddress = 0x1007 - offset; expectedBytes[j] = bytes[bigEndianAddress - 0x1000]; } } else { expectedBytes[j] = 0; } } var expectedValue = BitConverter.ToUInt32(expectedBytes, 0); Assert.IsTrue(device.TryReadAccess(readingAddress, out var result, SysbusAccessWidth.DoubleWord)); Assert.AreEqual(result, expectedValue); readingAddress++; } } #endregion Read tests #region Inheritance tests [Test] public void ShouldConcatenateAddressOffsets() { var baseAddress = 0x10000; int[] addressOffset = { 0x1000, 0x100, 0x10 }; var finalAddressOffset = 0x11110u; SetUpDeviceWithInfix($@" Peripheral1 {baseAddress} read-write C0 {addressOffset[0]} C1 {addressOffset[1]} REG1 {addressOffset[2]} 0x1000 "); Assert.IsTrue(device.TryReadAccess(finalAddressOffset, out var result, SysbusAccessWidth.DoubleWord)); Assert.AreEqual(result, 0x1000); } [Test] public void ShouldInheritSettingsFromParentPeripheral() { SetUpDeviceWithInfix(@" Peripheral1 0x1000 11 0xA5445E63 read-writeOnce REG1 0x0 REG2 0x10 27 REG3 0x20 0x12345678 27 "); Assert.IsTrue(device.TryReadAccess(0x1000, out var result, SysbusAccessWidth.DoubleWord)); Assert.AreEqual(result, 0x00000663); Assert.IsTrue(device.TryReadAccess(0x1010, out result, SysbusAccessWidth.DoubleWord)); Assert.AreEqual(result, 0x05445E63); Assert.IsTrue(device.TryReadAccess(0x1020, out result, SysbusAccessWidth.DoubleWord)); Assert.AreEqual(result, 0x02345678); } [Test] public void ShouldInheritSettingsFromParentCluster() { SetUpDeviceWithInfix(@" Peripheral1 0x1000 32 0xFFFFFFFF read-writeOnce Cluster1 0x100 0xA5445E63 11 REG1 0x0 REG2 0x10 27 REG3 0x20 0x12345678 27 "); Assert.IsTrue(device.TryReadAccess(0x1100, out var result, SysbusAccessWidth.DoubleWord)); Assert.AreEqual(result, 0x00000663); Assert.IsTrue(device.TryReadAccess(0x1110, out result, SysbusAccessWidth.DoubleWord)); Assert.AreEqual(result, 0x05445E63); Assert.IsTrue(device.TryReadAccess(0x1120, out result, SysbusAccessWidth.DoubleWord)); Assert.AreEqual(result, 0x02345678); } [Test] public void ShouldInheritSettingsFromParentClusterAndPeripheral() { SetUpDeviceWithInfix(@" Peripheral1 0x1000 11 0xFFFFFFFF read-writeOnce REG1 0x0 Cluster1 0x100 0xA5445E63 27 REG2 0x10 REG3 0x20 0x12345678 "); Assert.IsTrue(device.TryReadAccess(0x1000, out var result, SysbusAccessWidth.DoubleWord)); Assert.AreEqual(result, 0x000007FF); Assert.IsTrue(device.TryReadAccess(0x1110, out result, SysbusAccessWidth.DoubleWord)); Assert.AreEqual(result, 0x05445E63); Assert.IsTrue(device.TryReadAccess(0x1120, out result, SysbusAccessWidth.DoubleWord)); Assert.AreEqual(result, 0x02345678); } #endregion Inheritance tests #region Deriving tests [Test] public void ShouldThrowOnDerivingFromNonexistingRegister() { Assert.Throws(() => { SetUpDeviceWithInfix(@" Peripheral1 0x1000 read-write REG1 0x0 32 0xF2468ACE REG2 0x10 "); device.TryReadAccess(0x1010, out var result, SysbusAccessWidth.DoubleWord); }); } [Test] public void ShouldDeriveFromRegisterInTheSameScope() { SetUpDeviceWithInfix(@" Peripheral1 0x1000 read-write REG1 0x0 32 0xF2468ACE REG2 0x10 "); Assert.IsTrue(device.TryReadAccess(0x1010, out var result, SysbusAccessWidth.DoubleWord)); Assert.AreEqual(result, 0xF2468ACE); } [Test] public void ShouldDeriveFromRegisterInDifferentCluster() { SetUpDeviceWithInfix(@" Peripheral1 0x1000 read-write C0 0x0 REG1 0x0 32 0xF2468ACE REG2 0x10 "); Assert.IsTrue(device.TryReadAccess(0x1010, out var result, SysbusAccessWidth.DoubleWord)); Assert.AreEqual(result, 0xF2468ACE); } [Test] public void ShouldDeriveFromRegisterInDifferentPeripheral() { SetUpDeviceWithInfix(@" Peripheral1 0x1000 read-write C0 32 0x0 REG1 0x0 0xF2468ACE Peripheral2 0x2000 read-write REG2 0x10 "); Assert.IsTrue(device.TryReadAccess(0x2010, out var result, SysbusAccessWidth.DoubleWord)); Assert.AreEqual(result, 0xF2468ACE); } #endregion Deriving tests private void SetUpDeviceWithInfix(string infix, bool littleEndian = true) { var fileName = TemporaryFilesManager.Instance.GetTemporaryFile(); File.WriteAllText(fileName, (littleEndian ? Prefix : BigEndianPrefix) + infix + Postfix); device = new SVDParser(fileName, currentMachine.SystemBus); } private void SetUpDeviceWithString(string content) { var fileName = TemporaryFilesManager.Instance.GetTemporaryFile(); File.WriteAllText(fileName, content); device = new SVDParser(fileName, currentMachine.SystemBus); } private Machine currentMachine; private SVDParser device; private const string Prefix = @" Test description Test name 32 read-write 0xA5A5A5A5 "; private const string BigEndianPrefix = @" Test description Test name 32 read-write 0xA5A5A5A5 big "; private const string Postfix = @" "; } }