1 //
2 // Copyright (c) 2010-2018 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.Utilities;
10 using System.IO;
11 using Antmicro.Renode.Peripherals.Bus;
12 using Machine = Antmicro.Renode.Core.Machine;
13 using Antmicro.Renode.Exceptions;
14 
15 namespace Antmicro.Renode.UnitTests
16 {
17     [TestFixture]
18     public class SVDParserTests
19     {
20         [OneTimeSetUp]
Init()21         public void Init()
22         {
23             currentMachine = new Machine();
24         }
25 
26         #region Format tests
27 
28         [Test]
ShouldThrowOnNonexistingFile()29         public void ShouldThrowOnNonexistingFile()
30         {
31             Assert.Throws<RecoverableException>(() =>
32             {
33                 currentMachine = new Machine();
34                 device = new SVDParser(Path.Combine("invalid", "path.svd"), currentMachine.SystemBus);
35             });
36         }
37 
38         [Test]
ShouldThrowOnEmptyFile()39         public void ShouldThrowOnEmptyFile()
40         {
41             Assert.Throws<RecoverableException>(() =>
42             {
43                 SetUpDeviceWithString("");
44             });
45         }
46 
47         [Test]
ShouldThrowOnInvalidXml()48         public void ShouldThrowOnInvalidXml()
49         {
50             Assert.Throws<RecoverableException>(() =>
51             {
52                 SetUpDeviceWithString("Lorem ipsum...");
53             });
54         }
55 
56         [Test]
ShouldThrowOnInvalidSvd()57         public void ShouldThrowOnInvalidSvd()
58         {
59             Assert.Throws<RecoverableException>(() =>
60             {
61                 SetUpDeviceWithString(@"<?xml version=""1.0"" encoding=""utf-8"" standalone=""no""?>
62                     <invalidTag>
63                     </invalidTag>
64                 ");
65             });
66         }
67 
68         [Test]
ShouldThrowOnDeviceWithNoMandatoryFields()69         public void ShouldThrowOnDeviceWithNoMandatoryFields()
70         {
71             Assert.Throws<RecoverableException>(() =>
72             {
73                 SetUpDeviceWithString(@"<?xml version=""1.0"" encoding=""utf-8"" standalone=""no""?>
74                     <device>
75                     </device>
76                 ");
77             });
78         }
79 
80         [Test]
ShouldThrowOnDeviceWithNoPeripheralsTag()81         public void ShouldThrowOnDeviceWithNoPeripheralsTag()
82         {
83             Assert.Throws<RecoverableException>(() =>
84             {
85                 SetUpDeviceWithInfix("");
86             });
87         }
88 
89         [Test]
ShouldThrowOnDeviceWithNotEveryMandatoryField()90         public void ShouldThrowOnDeviceWithNotEveryMandatoryField()
91         {
92             // Both tags <description> and <name> are mandatory.
93             string[] mandatories = { "descripion", "name" };
94             foreach(var item in mandatories)
95             {
96                 Assert.Throws<RecoverableException>(() =>
97                 {
98                     SetUpDeviceWithString($@"<?xml version=""1.0"" encoding=""utf-8"" standalone=""no""?>
99                         <device>
100                             <{item}>value</{item}>
101                         </device>"
102                     );
103                 });
104             }
105         }
106 
107         [Test]
ShouldThrowOnCpuWithoutEndianness()108         public void ShouldThrowOnCpuWithoutEndianness()
109         {
110             // If cpu is defined the tag <endian> is mandatory.
111             Assert.Throws<RecoverableException>(() =>
112             {
113                 SetUpDeviceWithString($@"<?xml version=""1.0"" encoding=""utf-8"" standalone=""no""?>
114                     <device>
115                         <description>Test description</description>
116                         <name>Test name</name>
117                         <cpu>
118                         </cpu>
119                     </device>"
120                 );
121             });
122         }
123 
124         [Test]
ShouldThrowOnPeripheralWithNotEveryMandatoryField([Values(R, R)] string tag)125         public void ShouldThrowOnPeripheralWithNotEveryMandatoryField([Values("name", "baseAddress")] string tag)
126         {
127             // Both tags <name> and <baseAddress> are mandatory.
128             // 0x1000 is used as a field value so we do not have to care about field types in this test.
129             Assert.Throws<RecoverableException>(() =>
130             {
131                 SetUpDeviceWithInfix($@"
132                     <peripherals>
133                         <peripheral>
134                             <{tag}>0x1000</{tag}>
135                         </peripheral>
136                     </peripherals>
137                 ");
138             });
139         }
140 
141         [Test]
ShouldThrowOnClusterWithoutMandatoryFields()142         public void ShouldThrowOnClusterWithoutMandatoryFields()
143         {
144             // Tag <addressOffset> is mandatory.
145             Assert.Throws<RecoverableException>(() =>
146             {
147                 SetUpDeviceWithInfix(@"
148                     <peripherals>
149                         <peripheral>
150                             <name>Peripheral1</name>
151                             <baseAddress>0x1000</baseAddress>
152                             <registers>
153                                 <cluster>
154                                     <!--addressOffset and name missing-->
155                                     <register>
156                                         <name>REG1</name>
157                                         <addressOffset>0x0</addressOffset>
158                                         <resetValue>0</resetValue>
159                                     </register>
160                                 </cluster>
161                             </registers>
162                         </peripheral>
163                     </peripherals>
164                 ");
165             });
166         }
167 
168         [Test]
ShouldThrowOnRegisterWithNotEveryMandatoryField([Values(R, R)] string tag)169         public void ShouldThrowOnRegisterWithNotEveryMandatoryField([Values("name", "addressOffset")] string tag)
170         {
171             // Both tags <name> and <addressOffset> are mandatory.
172             // 0x1000 is used as a field value so we do not have to care about field types in this test.
173             Assert.Throws<RecoverableException>(() =>
174             {
175                 SetUpDeviceWithInfix($@"
176                     <peripherals>
177                         <peripheral>
178                             <name>Peripheral1</name>
179                             <baseAddress>0x1000</baseAddress>
180                             <registers>
181                                 <cluster>
182                                     <addressOffset>0</addressOffset>
183                                     <register>
184                                         <{tag}>0x1000</{tag}>
185                                     </register>
186                                 </cluster>
187                             </registers>
188                         </peripheral>
189                     </peripherals>
190                 ");
191             });
192         }
193 
194         [Test]
ShouldThrowOnDeviceWithInvalidSize()195         public void ShouldThrowOnDeviceWithInvalidSize()
196         {
197             Assert.Throws<RecoverableException>(() =>
198             {
199                 SetUpDeviceWithString(@"<?xml version=""1.0"" encoding=""utf-8"" standalone=""no""?>
200                     <device>
201                         <description>Test description</description>
202                         <name>Test name</name>
203                         <size>invalidValue</size>
204                     </device>"
205                 );
206             });
207         }
208 
209         [Test]
ShouldThrowOnDeviceWithInvalidResetValue()210         public void ShouldThrowOnDeviceWithInvalidResetValue()
211         {
212             Assert.Throws<RecoverableException>(() =>
213             {
214                 SetUpDeviceWithString(@"<?xml version=""1.0"" encoding=""utf-8"" standalone=""no""?>
215                     <device>
216                         <description>Test description</description>
217                         <name>Test name</name>
218                         <resetValue>invalidValue</resetValue>
219                         <peripherals>
220                         </peripherals>
221                     </device>"
222                 );
223             });
224         }
225 
226         [Test]
ShouldThrowOnDeviceWithInvalidAccess()227         public void ShouldThrowOnDeviceWithInvalidAccess()
228         {
229             Assert.Throws<RecoverableException>(() =>
230             {
231                 SetUpDeviceWithString(@"<?xml version=""1.0"" encoding=""utf-8"" standalone=""no""?>
232                     <device>
233                         <description>Test description</description>
234                         <name>Test name</name>
235                         <access>invalidValue</access>
236                     </device>"
237                 );
238             });
239         }
240 
241         [Test]
ShouldThrowOnCpuWithInvalidEndianness()242         public void ShouldThrowOnCpuWithInvalidEndianness()
243         {
244             Assert.Throws<RecoverableException>(() =>
245             {
246                 SetUpDeviceWithString($@"<?xml version=""1.0"" encoding=""utf-8"" standalone=""no""?>
247                     <device>
248                         <description>Test description</description>
249                         <name>Test name</name>
250                         <cpu>
251                             <endian>invalidValue</endian>
252                         </cpu>
253                         <peripherals>
254                         </peripherals>
255                     </device>"
256                 );
257             });
258         }
259 
260         [Test]
ShouldThrowOnRegisterWithoutDeterminedSizeAndResetValue()261         public void ShouldThrowOnRegisterWithoutDeterminedSizeAndResetValue()
262         {
263             Assert.Throws<RecoverableException>(() =>
264             {
265                 SetUpDeviceWithString($@"<?xml version=""1.0"" encoding=""utf-8"" standalone=""no""?>
266                 <device>
267                     <description>Test description</description>
268                     <name>Test name</name>
269                     <access>read-write</access>
270                     <peripherals>
271                         <peripheral>
272                             <name>name</name>
273                             <baseAddress>0</baseAddress>
274                             <registers>
275                                 <register>
276                                     <name>REG1</name>
277                                     <addressOffset>0</addressOffset>
278                                 </register>
279                             </registers>
280                         </peripheral>
281                     </peripherals>
282                 </device>"
283                 );
284             });
285         }
286 
287         #endregion Format tests
288 
289         #region Read tests
290 
291         [Test]
ShouldReadValueFromRegister()292         public void ShouldReadValueFromRegister()
293         {
294             var variableValue = 0xDEADBEEF;
295             SetUpDeviceWithInfix($@"
296                 <peripherals>
297                     <peripheral>
298                         <name>Peripheral1</name>
299                         <baseAddress>0x1000</baseAddress>
300                         <registers>
301                             <register>
302                                 <name>REG1</name>
303                                 <addressOffset>0x0</addressOffset>
304                                 <resetValue>{variableValue}</resetValue>
305                             </register>
306                         </registers>
307                     </peripheral>
308                 </peripherals>
309             ");
310             Assert.IsTrue(device.TryReadAccess(0x1000, out var result, SysbusAccessWidth.DoubleWord));
311             Assert.AreEqual(result, variableValue);
312         }
313 
314         [Test]
ShouldReadValueFromRegisterInBigEndian()315         public void ShouldReadValueFromRegisterInBigEndian()
316         {
317             var variableValue = 0xDEADBEEF;
318             byte[] bytes = BitConverter.GetBytes(variableValue);
319             SetUpDeviceWithInfix($@"
320                 <peripherals>
321                     <peripheral>
322                         <name>Peripheral1</name>
323                         <baseAddress>0x1000</baseAddress>
324                         <registers>
325                             <register>
326                                 <name>REG1</name>
327                                 <addressOffset>0x0</addressOffset>
328                                 <resetValue>{variableValue}</resetValue>
329                             </register>
330                         </registers>
331                     </peripheral>
332                 </peripherals>
333                 ",
334                 false
335             );
336 
337             byte[] newBytes = { bytes[3], bytes[2], bytes[1], bytes[0] };
338             var expectedValue = BitConverter.ToUInt32(newBytes, 0);
339             Assert.IsTrue(device.TryReadAccess(0x1000, out var result, SysbusAccessWidth.DoubleWord));
340             Assert.AreEqual(result, expectedValue);
341         }
342 
343         [Test]
ShouldHandleDifferentAccessPermissions([Values(R, R, R, R, R)] string access)344         public void ShouldHandleDifferentAccessPermissions([Values("write-only", "read-only", "read-write", "read-writeOnce", "writeOnce")] string access)
345         {
346             SetUpDeviceWithInfix($@"
347                 <peripherals>
348                     <peripheral>
349                         <name>Peripheral1</name>
350                         <baseAddress>0x1000</baseAddress>
351                         <registers>
352                             <register>
353                                 <name>REG1</name>
354                                 <addressOffset>0x0</addressOffset>
355                                 <resetValue>0x01234567</resetValue>
356                                 <access>{access}</access>
357                             </register>
358                         </registers>
359                     </peripheral>
360                 </peripherals>
361             ");
362             var returnValue = device.TryReadAccess(0x1000, out var result, SysbusAccessWidth.DoubleWord);
363             if(access == "write-only" || access == "writeOnce")
364             {
365                 Assert.AreEqual(result, 0);
366                 Assert.IsTrue(returnValue);
367             }
368             else
369             {
370                 Assert.AreEqual(result, 0x01234567);
371                 Assert.IsTrue(returnValue);
372             }
373         }
374 
375         [Test]
ShouldReadFromRegistersOfDifferentSizes()376         public void ShouldReadFromRegistersOfDifferentSizes()
377         {
378             var variableValue = 0xDEADBEEF;
379             var maxSize = 32;
380             for(var i = 1; i < maxSize; i++)
381             {
382                 SetUpDeviceWithInfix($@"
383                     <peripherals>
384                         <peripheral>
385                             <name>Peripheral1</name>
386                             <baseAddress>0x1000</baseAddress>
387                             <registers>
388                                 <register>
389                                    <name>REG1</name>
390                                     <addressOffset>0x0</addressOffset>
391                                     <size>{i}</size>
392                                     <resetValue>{variableValue}</resetValue>
393                                     <access>read-write</access>
394                                 </register>
395                             </registers>
396                         </peripheral>
397                     </peripherals>
398                 ");
399                 var mask = (uint)((1ul << i) - 1);
400                 var expectedValue = variableValue & mask;
401                 Assert.IsTrue(device.TryReadAccess(0x1000, out var result, SysbusAccessWidth.DoubleWord));
402                 Assert.AreEqual(result, expectedValue);
403             }
404         }
405 
406         [Test]
ShouldReadFromUnalignedOffsetInOnePeripheral()407         public void ShouldReadFromUnalignedOffsetInOnePeripheral()
408         {
409             byte[] bytes = { 11, 22, 33, 44, 55, 66, 77, 88 };
410             SetUpDeviceWithInfix($@"
411                 <peripherals>
412                     <peripheral>
413                         <access>read-write</access>
414                         <name>Peripheral1</name>
415                         <baseAddress>0x1000</baseAddress>
416                         <registers>
417                             <register>
418                                 <name>REG1</name>
419                                 <addressOffset>0x0</addressOffset>
420                                 <resetValue>{BitConverter.ToUInt32(bytes, 0)}</resetValue>
421                                 <size>32</size>
422                             </register>
423                             <register>
424                                 <name>REG2</name>
425                                 <addressOffset>0x4</addressOffset>
426                                 <resetValue>{bytes[4]}</resetValue>
427                                 <size>8</size>
428                             </register>
429                             <register>
430                                 <name>REG3</name>
431                                 <addressOffset>0x5</addressOffset>
432                                 <resetValue>{bytes[5]}</resetValue>
433                                 <size>8</size>
434                             </register>
435                             <register>
436                                 <name>REG4</name>
437                                 <addressOffset>0x6</addressOffset>
438                                 <resetValue>{BitConverter.ToInt16(bytes, 6)}</resetValue>
439                                 <size>16</size>
440                             </register>
441                         </registers>
442                     </peripheral>
443                 </peripherals>
444             ");
445 
446             for(var i = -3; i < 8; i++)
447             {
448                 var readingAddress = (ulong)(0x1000 + i);
449                 var expectedBytes = new byte[4];
450                 for(var j = 0u; j < 4; j++)
451                 {
452                     if(readingAddress + j >= 0x1000 && readingAddress + j < 0x1008)
453                     {
454                         expectedBytes[j] = bytes[readingAddress + j - 0x1000];
455                     }
456                     else
457                     {
458                         expectedBytes[j] = 0;
459                     }
460                 }
461                 var expectedValue = BitConverter.ToUInt32(expectedBytes, 0);
462                 Assert.IsTrue(device.TryReadAccess(readingAddress, out var result, SysbusAccessWidth.DoubleWord));
463                 Assert.AreEqual(result, expectedValue);
464             }
465         }
466 
467         [Test]
ShouldReadFromUnalignedOffsetInManyPeripherals()468         public void ShouldReadFromUnalignedOffsetInManyPeripherals()
469         {
470             byte[] bytes = { 11, 22, 33, 44, 55, 66, 77, 88 };
471             SetUpDeviceWithInfix($@"
472                 <peripherals>
473 
474                     <peripheral>
475                         <access>read-write</access>
476                         <name>Peripheral1</name>
477                         <baseAddress>0x1000</baseAddress>
478                         <registers>
479                             <register>
480                                 <name>REG1</name>
481                                 <addressOffset>0x0</addressOffset>
482                                 <resetValue>{BitConverter.ToUInt32(bytes, 0)}</resetValue>
483                                 <size>32</size>
484                             </register>
485                         </registers>
486                     </peripheral>
487 
488                     <peripheral>
489                         <access>read-write</access>
490                         <name>Peripheral2</name>
491                         <baseAddress>0x1004</baseAddress>
492                         <registers>
493                             <register>
494                                 <name>REG2</name>
495                                 <addressOffset>0</addressOffset>
496                                 <resetValue>{bytes[4]}</resetValue>
497                                 <size>8</size>
498                             </register>
499                             <register>
500                                 <name>REG3</name>
501                                 <addressOffset>1</addressOffset>
502                                 <resetValue>{bytes[5]}</resetValue>
503                                 <size>8</size>
504                             </register>
505                         </registers>
506                     </peripheral>
507 
508                     <peripheral>
509                         <access>read-write</access>
510                         <name>Peripheral3</name>
511                         <baseAddress>0x1006</baseAddress>
512                         <registers>
513                             <register>
514                                 <name>REG4</name>
515                                 <addressOffset>0</addressOffset>
516                                 <resetValue>{BitConverter.ToInt16(bytes, 6)}</resetValue>
517                                 <size>16</size>
518                             </register>
519                         </registers>
520                     </peripheral>
521                 </peripherals>
522             ");
523 
524             for(var i = -3; i < 8; i++)
525             {
526                 var readingAddress = (ulong)(0x1000 + i);
527                 var expectedBytes = new byte[4];
528                 for(var j = 0u; j < 4; j++)
529                 {
530                     if(readingAddress + j >= 0x1000 && readingAddress + j < 0x1008)
531                     {
532                         expectedBytes[j] = bytes[readingAddress + j - 0x1000];
533                     }
534                     else
535                     {
536                         expectedBytes[j] = 0;
537                     }
538                 }
539                 var expectedValue = BitConverter.ToUInt32(expectedBytes, 0);
540 
541                 Assert.IsTrue(device.TryReadAccess(readingAddress, out var result, SysbusAccessWidth.DoubleWord));
542                 Assert.AreEqual(result, expectedValue);
543             }
544         }
545 
546         [Test]
ShouldReadFromUnalignedOffsetInOnePeripheralInBigEndian()547         public void ShouldReadFromUnalignedOffsetInOnePeripheralInBigEndian()
548         {
549             byte[] bytes = { 11, 22, 33, 44, 55, 66, 77, 88 };
550             SetUpDeviceWithInfix($@"
551                 <peripherals>
552                     <peripheral>
553                         <access>read-write</access>
554                         <name>Peripheral1</name>
555                         <baseAddress>0x1000</baseAddress>
556                         <registers>
557                             <register>
558                                 <name>REG1</name>
559                                 <addressOffset>0x0</addressOffset>
560                                 <resetValue>{BitConverter.ToUInt32(bytes, 0)}</resetValue>
561                                 <size>32</size>
562                             </register>
563                             <register>
564                                 <name>REG2</name>
565                                 <addressOffset>0x4</addressOffset>
566                                 <resetValue>{bytes[4]}</resetValue>
567                                 <size>8</size>
568                             </register>
569                             <register>
570                                 <name>REG3</name>
571                                 <addressOffset>0x5</addressOffset>
572                                 <resetValue>{bytes[5]}</resetValue>
573                                 <size>8</size>
574                             </register>
575                             <register>
576                                 <name>REG4</name>
577                                 <addressOffset>0x6</addressOffset>
578                                 <resetValue>{BitConverter.ToInt16(bytes, 6)}</resetValue>
579                                 <size>16</size>
580                             </register>
581                         </registers>
582                     </peripheral>
583                 </peripherals>
584                 ",
585                 false
586             );
587             var readingAddress = (ulong)(0x1000 - 3);
588             for(var i = -3; i < 8; i++)
589             {
590                 var expectedBytes = new byte[4];
591                 for(var j = 0u; j < 4; j++)
592                 {
593                     var tmpAddres = readingAddress + j;
594                     if(tmpAddres >= 0x1000 && tmpAddres < 0x1008)
595                     {
596                         if(tmpAddres <= 0x1003)
597                         {
598                             var offset = tmpAddres - 0x1000;
599                             var bigEndianAddress = 0x1003 - offset;
600                             expectedBytes[j] = bytes[bigEndianAddress - 0x1000];
601                         }
602                         else if(tmpAddres == 0x1004)
603                         {
604                             expectedBytes[j] = bytes[tmpAddres - 0x1000];
605                         }
606                         else if(tmpAddres == 0x1005)
607                         {
608                             expectedBytes[j] = bytes[tmpAddres - 0x1000];
609                         }
610                         else
611                         {
612                             var offset = tmpAddres - 0x1006;
613                             var bigEndianAddress = 0x1007 - offset;
614                             expectedBytes[j] = bytes[bigEndianAddress - 0x1000];
615                         }
616                     }
617                     else
618                     {
619                         expectedBytes[j] = 0;
620                     }
621                 }
622                 var expectedValue = BitConverter.ToUInt32(expectedBytes, 0);
623                 Assert.IsTrue(device.TryReadAccess(readingAddress, out var result, SysbusAccessWidth.DoubleWord));
624                 Assert.AreEqual(result, expectedValue);
625                 readingAddress++;
626             }
627         }
628 
629         [Test]
ShouldReadFromUnalignedOffsetInOnePeripheralWithWriteOnlyRegister()630         public void ShouldReadFromUnalignedOffsetInOnePeripheralWithWriteOnlyRegister()
631         {
632             byte[] bytes = { 11, 22, 33, 44, 55, 66, 77, 88 };
633             SetUpDeviceWithInfix($@"
634                 <peripherals>
635                     <peripheral>
636                         <access>read-write</access>
637                         <name>Peripheral1</name>
638                         <baseAddress>0x1000</baseAddress>
639                         <registers>
640                             <register>
641                                 <name>REG1</name>
642                                 <addressOffset>0x0</addressOffset>
643                                 <resetValue>{BitConverter.ToUInt32(bytes, 0)}</resetValue>
644                                 <size>32</size>
645                             </register>
646                             <register>
647                                 <name>REG2</name>
648                                 <addressOffset>0x4</addressOffset>
649                                 <resetValue>{bytes[4]}</resetValue>
650                                 <access>write-only</access>
651                                 <size>8</size>
652                             </register>
653                             <register>
654                                 <name>REG3</name>
655                                 <addressOffset>0x5</addressOffset>
656                                 <resetValue>{bytes[5]}</resetValue>
657                                 <access>write-only</access>
658                                 <size>8</size>
659                             </register>
660                             <register>
661                                 <name>REG4</name>
662                                 <addressOffset>0x6</addressOffset>
663                                 <resetValue>{BitConverter.ToInt16(bytes, 6)}</resetValue>
664                                 <size>16</size>
665                             </register>
666                         </registers>
667                     </peripheral>
668                 </peripherals>
669                 ",
670                 false
671             );
672             var readingAddress = (ulong)(0x1000 - 3);
673             for(var i = -3; i < 8; i++)
674             {
675                 var expectedBytes = new byte[4];
676                 for(var j = 0u; j < 4; j++)
677                 {
678                     var tmpAddres = readingAddress + j;
679                     if(tmpAddres >= 0x1000 && tmpAddres < 0x1008)
680                     {
681                         if(tmpAddres == 0x1004 || tmpAddres == 0x1005)
682                         {
683                             expectedBytes[j] = 0;
684                         }
685                         else if(tmpAddres <= 0x1003)
686                         {
687                             var offset = tmpAddres - 0x1000;
688                             var bigEndianAddress = 0x1003 - offset;
689                             expectedBytes[j] = bytes[bigEndianAddress - 0x1000];
690                         }
691                         else if(tmpAddres == 0x1004)
692                         {
693                             expectedBytes[j] = bytes[tmpAddres - 0x1000];
694                         }
695                         else if(tmpAddres == 0x1005)
696                         {
697                             expectedBytes[j] = bytes[tmpAddres - 0x1000];
698                         }
699                         else
700                         {
701                             var offset = tmpAddres - 0x1006;
702                             var bigEndianAddress = 0x1007 - offset;
703                             expectedBytes[j] = bytes[bigEndianAddress - 0x1000];
704                         }
705                     }
706                     else
707                     {
708                         expectedBytes[j] = 0;
709                     }
710                 }
711                 var expectedValue = BitConverter.ToUInt32(expectedBytes, 0);
712                 Assert.IsTrue(device.TryReadAccess(readingAddress, out var result, SysbusAccessWidth.DoubleWord));
713                 Assert.AreEqual(result, expectedValue);
714                 readingAddress++;
715             }
716         }
717 
718         #endregion Read tests
719 
720         #region Inheritance tests
721 
722         [Test]
ShouldConcatenateAddressOffsets()723         public void ShouldConcatenateAddressOffsets()
724         {
725             var baseAddress = 0x10000;
726             int[] addressOffset = { 0x1000, 0x100, 0x10 };
727             var finalAddressOffset = 0x11110u;
728 
729             SetUpDeviceWithInfix($@"
730                 <peripherals>
731                     <peripheral>
732                         <name>Peripheral1</name>
733                         <baseAddress>{baseAddress}</baseAddress>
734                         <access>read-write</access>
735                         <registers>
736                             <cluster>
737                                 <name>C0</name>
738                                 <addressOffset>{addressOffset[0]}</addressOffset>
739                                 <cluster>
740                                     <name>C1</name>
741                                     <addressOffset>{addressOffset[1]}</addressOffset>
742                                     <register>
743                                         <name>REG1</name>
744                                         <addressOffset>{addressOffset[2]}</addressOffset>
745                                         <resetValue>0x1000</resetValue>
746                                     </register>
747                                 </cluster>
748                             </cluster>
749                         </registers>
750                     </peripheral>
751                 </peripherals>
752             ");
753             Assert.IsTrue(device.TryReadAccess(finalAddressOffset, out var result, SysbusAccessWidth.DoubleWord));
754             Assert.AreEqual(result, 0x1000);
755         }
756 
757         [Test]
ShouldInheritSettingsFromParentPeripheral()758         public void ShouldInheritSettingsFromParentPeripheral()
759         {
760             SetUpDeviceWithInfix(@"
761                 <peripherals>
762                     <peripheral>
763                         <name>Peripheral1</name>
764                         <baseAddress>0x1000</baseAddress>
765                         <size>11</size>
766                         <resetValue>0xA5445E63</resetValue>
767                         <access>read-writeOnce</access>
768                         <registers>
769                             <register>
770                                 <name>REG1</name>
771                                 <addressOffset>0x0</addressOffset>
772                             </register>
773                             <register>
774                                 <name>REG2</name>
775                                 <addressOffset>0x10</addressOffset>
776                                 <size>27</size>
777                             </register>
778                             <register>
779                                 <name>REG3</name>
780                                 <addressOffset>0x20</addressOffset>
781                                 <resetValue>0x12345678</resetValue>
782                                 <size>27</size>
783                             </register>
784                         </registers>
785                     </peripheral>
786                 </peripherals>
787             ");
788             Assert.IsTrue(device.TryReadAccess(0x1000, out var result, SysbusAccessWidth.DoubleWord));
789             Assert.AreEqual(result, 0x00000663);
790             Assert.IsTrue(device.TryReadAccess(0x1010, out result, SysbusAccessWidth.DoubleWord));
791             Assert.AreEqual(result, 0x05445E63);
792             Assert.IsTrue(device.TryReadAccess(0x1020, out result, SysbusAccessWidth.DoubleWord));
793             Assert.AreEqual(result, 0x02345678);
794         }
795 
796         [Test]
ShouldInheritSettingsFromParentCluster()797         public void ShouldInheritSettingsFromParentCluster()
798         {
799             SetUpDeviceWithInfix(@"
800                 <peripherals>
801                     <peripheral>
802                         <name>Peripheral1</name>
803                         <baseAddress>0x1000</baseAddress>
804                         <size>32</size>
805                         <resetValue>0xFFFFFFFF</resetValue>
806                         <access>read-writeOnce</access>
807                         <registers>
808                             <cluster>
809                                 <name>Cluster1</name>
810                                 <addressOffset>0x100</addressOffset>
811                                 <resetValue>0xA5445E63</resetValue>
812                                 <size>11</size>
813                                 <register>
814                                     <name>REG1</name>
815                                     <addressOffset>0x0</addressOffset>
816                                 </register>
817                                 <register>
818                                     <name>REG2</name>
819                                     <addressOffset>0x10</addressOffset>
820                                     <size>27</size>
821                                 </register>
822                                 <register>
823                                     <name>REG3</name>
824                                     <addressOffset>0x20</addressOffset>
825                                     <resetValue>0x12345678</resetValue>
826                                     <size>27</size>
827                                 </register>
828                             </cluster>
829                         </registers>
830                     </peripheral>
831                 </peripherals>
832             ");
833             Assert.IsTrue(device.TryReadAccess(0x1100, out var result, SysbusAccessWidth.DoubleWord));
834             Assert.AreEqual(result, 0x00000663);
835             Assert.IsTrue(device.TryReadAccess(0x1110, out result, SysbusAccessWidth.DoubleWord));
836             Assert.AreEqual(result, 0x05445E63);
837             Assert.IsTrue(device.TryReadAccess(0x1120, out result, SysbusAccessWidth.DoubleWord));
838             Assert.AreEqual(result, 0x02345678);
839         }
840 
841         [Test]
ShouldInheritSettingsFromParentClusterAndPeripheral()842         public void ShouldInheritSettingsFromParentClusterAndPeripheral()
843         {
844             SetUpDeviceWithInfix(@"
845                 <peripherals>
846                     <peripheral>
847                         <name>Peripheral1</name>
848                         <baseAddress>0x1000</baseAddress>
849                         <size>11</size>
850                         <resetValue>0xFFFFFFFF</resetValue>
851                         <access>read-writeOnce</access>
852                         <registers>
853                             <register>
854                                 <name>REG1</name>
855                                 <addressOffset>0x0</addressOffset>
856                             </register>
857                             <cluster>
858                                 <name>Cluster1</name>
859                                 <addressOffset>0x100</addressOffset>
860                                 <resetValue>0xA5445E63</resetValue>
861                                 <size>27</size>
862                                 <register>
863                                     <name>REG2</name>
864                                     <addressOffset>0x10</addressOffset>
865                                 </register>
866                                 <register>
867                                     <name>REG3</name>
868                                     <addressOffset>0x20</addressOffset>
869                                     <resetValue>0x12345678</resetValue>
870                                 </register>
871                             </cluster>
872                         </registers>
873                     </peripheral>
874                 </peripherals>
875             ");
876             Assert.IsTrue(device.TryReadAccess(0x1000, out var result, SysbusAccessWidth.DoubleWord));
877             Assert.AreEqual(result, 0x000007FF);
878             Assert.IsTrue(device.TryReadAccess(0x1110, out result, SysbusAccessWidth.DoubleWord));
879             Assert.AreEqual(result, 0x05445E63);
880             Assert.IsTrue(device.TryReadAccess(0x1120, out result, SysbusAccessWidth.DoubleWord));
881             Assert.AreEqual(result, 0x02345678);
882         }
883 
884         #endregion Inheritance tests
885 
886         #region Deriving tests
887 
888         [Test]
ShouldThrowOnDerivingFromNonexistingRegister()889         public void ShouldThrowOnDerivingFromNonexistingRegister()
890         {
891             Assert.Throws<RecoverableException>(() =>
892             {
893                 SetUpDeviceWithInfix(@"
894                     <peripherals>
895                         <peripheral>
896                             <name>Peripheral1</name>
897                             <baseAddress>0x1000</baseAddress>
898                             <access>read-write</access>
899                             <registers>
900                                 <register>
901                                     <name>REG1</name>
902                                     <addressOffset>0x0</addressOffset>
903                                     <size>32</size>
904                                     <resetValue>0xF2468ACE</resetValue>
905                                 </register>
906                                 <register derivedFrom=""invalidObject"">
907                                     <name>REG2</name>
908                                     <addressOffset>0x10</addressOffset>
909                                 </register>
910                             </registers>
911                         </peripheral>
912                     </peripherals>
913                 ");
914                 device.TryReadAccess(0x1010, out var result, SysbusAccessWidth.DoubleWord);
915             });
916         }
917 
918         [Test]
ShouldDeriveFromRegisterInTheSameScope()919         public void ShouldDeriveFromRegisterInTheSameScope()
920         {
921             SetUpDeviceWithInfix(@"
922                 <peripherals>
923                     <peripheral>
924                         <name>Peripheral1</name>
925                         <baseAddress>0x1000</baseAddress>
926                         <access>read-write</access>
927                         <registers>
928                             <register>
929                                 <name>REG1</name>
930                                 <addressOffset>0x0</addressOffset>
931                                 <size>32</size>
932                                 <resetValue>0xF2468ACE</resetValue>
933                             </register>
934                             <register derivedFrom=""REG1"">
935                                 <name>REG2</name>
936                                 <addressOffset>0x10</addressOffset>
937                             </register>
938                         </registers>
939                     </peripheral>
940                 </peripherals>
941             ");
942             Assert.IsTrue(device.TryReadAccess(0x1010, out var result, SysbusAccessWidth.DoubleWord));
943             Assert.AreEqual(result, 0xF2468ACE);
944         }
945 
946         [Test]
ShouldDeriveFromRegisterInDifferentCluster()947         public void ShouldDeriveFromRegisterInDifferentCluster()
948         {
949             SetUpDeviceWithInfix(@"
950                 <peripherals>
951                     <peripheral>
952                         <name>Peripheral1</name>
953                         <baseAddress>0x1000</baseAddress>
954                         <access>read-write</access>
955                         <registers>
956                             <cluster>
957                                 <name>C0</name>
958                                 <addressOffset>0x0</addressOffset>
959                                 <register>
960                                     <name>REG1</name>
961                                     <addressOffset>0x0</addressOffset>
962                                     <size>32</size>
963                                     <resetValue>0xF2468ACE</resetValue>
964                                 </register>
965                             </cluster>
966                             <register derivedFrom=""Peripheral1.C0.REG1"">
967                                 <name>REG2</name>
968                                 <addressOffset>0x10</addressOffset>
969                             </register>
970                         </registers>
971                     </peripheral>
972                 </peripherals>
973             ");
974             Assert.IsTrue(device.TryReadAccess(0x1010, out var result, SysbusAccessWidth.DoubleWord));
975             Assert.AreEqual(result, 0xF2468ACE);
976         }
977 
978         [Test]
ShouldDeriveFromRegisterInDifferentPeripheral()979         public void ShouldDeriveFromRegisterInDifferentPeripheral()
980         {
981             SetUpDeviceWithInfix(@"
982                 <peripherals>
983                     <peripheral>
984                         <name>Peripheral1</name>
985                         <baseAddress>0x1000</baseAddress>
986                         <access>read-write</access>
987                         <registers>
988                             <cluster>
989                                 <name>C0</name>
990                                 <size>32</size>
991                                 <addressOffset>0x0</addressOffset>
992                                 <register>
993                                     <name>REG1</name>
994                                     <addressOffset>0x0</addressOffset>
995                                     <resetValue>0xF2468ACE</resetValue>
996                                 </register>
997                             </cluster>
998                         </registers>
999                     </peripheral>
1000                     <peripheral>
1001                         <name>Peripheral2</name>
1002                         <baseAddress>0x2000</baseAddress>
1003                         <access>read-write</access>
1004                         <registers>
1005                             <register derivedFrom=""Peripheral1.C0.REG1"">
1006                                 <name>REG2</name>
1007                                 <addressOffset>0x10</addressOffset>
1008                             </register>
1009                         </registers>
1010                     </peripheral>
1011                 </peripherals>
1012             ");
1013             Assert.IsTrue(device.TryReadAccess(0x2010, out var result, SysbusAccessWidth.DoubleWord));
1014             Assert.AreEqual(result, 0xF2468ACE);
1015         }
1016 
1017         #endregion Deriving tests
1018 
SetUpDeviceWithInfix(string infix, bool littleEndian = true)1019         private void SetUpDeviceWithInfix(string infix, bool littleEndian = true)
1020         {
1021             var fileName = TemporaryFilesManager.Instance.GetTemporaryFile();
1022             File.WriteAllText(fileName, (littleEndian ? Prefix : BigEndianPrefix) + infix + Postfix);
1023             device = new SVDParser(fileName, currentMachine.SystemBus);
1024         }
1025 
SetUpDeviceWithString(string content)1026         private void SetUpDeviceWithString(string content)
1027         {
1028             var fileName = TemporaryFilesManager.Instance.GetTemporaryFile();
1029             File.WriteAllText(fileName, content);
1030             device = new SVDParser(fileName, currentMachine.SystemBus);
1031         }
1032 
1033         private Machine currentMachine;
1034         private SVDParser device;
1035 
1036         private const string Prefix = @"<?xml version=""1.0"" encoding=""utf-8"" standalone=""no""?>
1037             <device>
1038                 <description>Test description</description>
1039                 <name>Test name</name>
1040                 <size>32</size>
1041                 <access>read-write</access>
1042                 <resetValue>0xA5A5A5A5</resetValue>
1043         ";
1044 
1045         private const string BigEndianPrefix = @"<?xml version=""1.0"" encoding=""utf-8"" standalone=""no""?>
1046             <device>
1047                 <description>Test description</description>
1048                 <name>Test name</name>
1049                 <size>32</size>
1050                 <access>read-write</access>
1051                 <resetValue>0xA5A5A5A5</resetValue>
1052                 <cpu>
1053                     <endian>big</endian>
1054                 </cpu>
1055         ";
1056 
1057         private const string Postfix = @"
1058             </device>
1059         ";
1060     }
1061 }
1062