1 //
2 // Copyright (c) 2010-2024 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 System.Xml.Linq;
9 using System.Linq;
10 using Antmicro.Renode.Logging;
11 using System.Collections.Generic;
12 using Antmicro.Renode.Exceptions;
13 using System.Xml;
14 using System.IO;
15 using Antmicro.Renode.Utilities;
16 using System.IO.Compression;
17 
18 namespace Antmicro.Renode.Peripherals.Bus
19 {
20     public sealed class SVDParser
21     {
SVDParser(string path, IBusController parent)22         public SVDParser(string path, IBusController parent)
23         {
24             currentSystemBus = parent;
25             XDocument document;
26             Stream possibleGzip;
27             try
28             {
29                 using(possibleGzip = File.OpenRead(path))
30                 {
31                     if(possibleGzip.ReadByte() == 0x1F && possibleGzip.ReadByte() == 0x8B) // gzip header
32                     {
33                         parent.Log(LogLevel.Info, "Detected gzipped file, ungzipping.");
34                         possibleGzip.Close();
35                         possibleGzip = new GZipStream(File.OpenRead(path), CompressionMode.Decompress);
36                         path = TemporaryFilesManager.Instance.GetTemporaryFile();
37                         using(var extractedFile = File.OpenWrite(path))
38                         {
39                             possibleGzip.CopyTo(extractedFile);
40                         }
41                         parent.Log(LogLevel.Info, "Successfully ungzipped.");
42                     }
43                 }
44                 using(var stream = File.OpenRead(path))
45                 {
46                     document = XDocument.Load(stream);
47                 }
48             }
49             catch(Exception ex)
50             {
51                 if(ex is FileNotFoundException || ex is DirectoryNotFoundException || ex is PathTooLongException)
52                 {
53                     throw new RecoverableException($"File '{path}' does not exist.");
54                 }
55                 else if(ex is UnauthorizedAccessException)
56                 {
57                     throw new RecoverableException($"File '{path}' cannot be loaded due to insufficient permissions.");
58                 }
59                 else if(ex is IOException)
60                 {
61                     throw new RecoverableException($"An I/O error occurred while opening the file '{path}'.");
62                 }
63                 else if(ex is XmlException)
64                 {
65                     throw new RecoverableException($"Given SVD file could not be loaded due to an exception: {ex.Message}.");
66                 }
67                 throw;
68             }
69             var deviceNode = document.Elements().FirstOrDefault(x => x.Name == "device");
70             if(deviceNode == null)
71             {
72                 throw new RecoverableException($"There is no <device> element in the file '{path}'.");
73             }
74             var device = new SVDDevice(deviceNode, this);
75 
76             registerDictionary = new Dictionary<ulong, SVDRegister>();
77             foreach(var register in device.Peripherals.SelectMany(x => x.Registers))
78             {
79                 AppendRegisterToDictionary(register);
80             }
81             parent.Log(LogLevel.Info, "Loaded SVD: {0}. Name: {1}. Description: {2}.", path, device.Name, device.Description);
82         }
83 
TryReadAccess(ulong offset, out ulong value, SysbusAccessWidth width)84         public bool TryReadAccess(ulong offset, out ulong value, SysbusAccessWidth width)
85         {
86             var bytesToRead = CheckAndGetWidth(width);
87             var weHaveIt = false;
88             value = 0;
89 
90             if(HitInTheRegisterDictionary(out var tmpRegister, offset, bytesToRead))
91             {
92                 weHaveIt = true;
93                 if(tmpRegister.HasReadAccess)
94                 {
95                     value = tmpRegister.ResetValue;
96                     LogReadSuccess(value, tmpRegister.Peripheral.Name, tmpRegister.Name, offset, width);
97                 }
98                 else
99                 {
100                     LogReadFail(tmpRegister.Peripheral.Name, tmpRegister.Name, offset, width);
101                 }
102             }
103             else
104             {
105                 value = AssembleValueFromRegisters(offset, width, ref weHaveIt);
106             }
107             return weHaveIt;
108         }
109 
TryWriteAccess(ulong offset, ulong value, SysbusAccessWidth width)110         public bool TryWriteAccess(ulong offset, ulong value, SysbusAccessWidth width)
111         {
112             int bytesToWrite = CheckAndGetWidth(width);
113             var weHaveIt = false;
114             if(HitInTheRegisterDictionary(out var tmpRegister, offset, bytesToWrite))
115             {
116                 weHaveIt = true;
117                 if(tmpRegister.HasWriteAccess || tmpRegister.HasWriteOnceAccess)
118                 {
119                     LogWriteSuccess(value, tmpRegister.Peripheral.Name, tmpRegister.Name, offset, width, tmpRegister.HasWriteOnceAccess);
120                 }
121                 else
122                 {
123                     LogWriteFail(value, tmpRegister.Peripheral.Name, tmpRegister.Name, offset, width);
124                 }
125             }
126             else
127             {
128                 LogWriteRequests(value, offset, width, ref weHaveIt);
129             }
130             return weHaveIt;
131         }
132 
HitInTheRegisterDictionary(out SVDRegister tmpRegister, ulong offset, int countOfBytes)133         private bool HitInTheRegisterDictionary(out SVDRegister tmpRegister, ulong offset, int countOfBytes)
134         {
135             return registerDictionary.TryGetValue(offset, out tmpRegister) && tmpRegister.Address == offset && tmpRegister.SizeInBytes == countOfBytes;
136         }
137 
LogWriteRequests(ulong value, ulong offset, SysbusAccessWidth width, ref bool weHaveIt)138         private void LogWriteRequests(ulong value, ulong offset, SysbusAccessWidth width, ref bool weHaveIt)
139         {
140             var sizeInBytes = (int)width;
141             for(var i = 0; i < sizeInBytes; i++)
142             {
143                 var tmpOffset = offset + (ulong)i;
144                 if(registerDictionary.TryGetValue(tmpOffset, out var register))
145                 {
146                     var howManyTimes = HowManyRequestsToTheRegister(register, sizeInBytes, offset, tmpOffset);
147                     var mask = ((1ul << (8 * howManyTimes)) - 1) << (8 * i);
148                     var tmpValue = (value & mask) >> (8 * i);
149                     if(register.HasWriteAccess || register.HasWriteOnceAccess)
150                     {
151                         weHaveIt = true;
152                         LogWriteSuccess(tmpValue, register.Peripheral.Name, register.Name, tmpOffset, width, register.Access == PermittedAccess.WriteOnce, offset, value);
153                     }
154                     else
155                     {
156                         LogWriteFail(tmpValue, register.Peripheral.Name, register.Name, tmpOffset, width, offset, value);
157                     }
158                     i += howManyTimes - 1;
159                 }
160             }
161         }
162 
AssembleValueFromRegisters(ulong offset, SysbusAccessWidth width, ref bool weHaveIt)163         private ulong AssembleValueFromRegisters(ulong offset, SysbusAccessWidth width, ref bool weHaveIt)
164         {
165             var sizeInBytes = (int)width;
166             var result = 0ul;
167             for(var i = 0; i < sizeInBytes; i++)
168             {
169                 var tmpOffset = offset + (ulong)i;
170                 if(registerDictionary.TryGetValue(tmpOffset, out var register))
171                 {
172                     var howManyTimes = HowManyRequestsToTheRegister(register, sizeInBytes, offset, tmpOffset);
173                     var mask = ((1ul << (8 * howManyTimes)) - 1) << (8 * (int)(tmpOffset - register.Address));
174                     var tmpValue = (register.ResetValue & mask) >> (8 * (int)(tmpOffset - register.Address));
175                     if(register.HasReadAccess)
176                     {
177                         weHaveIt = true;
178                         LogReadSuccess(tmpValue, register.Peripheral.Name, register.Name, tmpOffset, width, offset);
179                         result += tmpValue << (8 * i);
180                     }
181                     else
182                     {
183                         LogReadFail(register.Peripheral.Name, register.Name, tmpOffset, width, offset);
184                     }
185                     i += howManyTimes - 1;
186                 }
187             }
188             return result;
189         }
190 
HowManyRequestsToTheRegister(SVDRegister register, int sizeInBytes, ulong offset, ulong tmpOffset)191         private int HowManyRequestsToTheRegister(SVDRegister register, int sizeInBytes, ulong offset, ulong tmpOffset)
192         {
193             var registerLastAddress = register.Address + register.SizeInBytes - 1;
194             var lastReadingAddress = offset + checked((ulong)sizeInBytes) - 1;
195             var diff = (int)(registerLastAddress - lastReadingAddress);
196             int howManyTimes = (int)(registerLastAddress - tmpOffset + 1) - (diff > 0 ? diff : 0);
197             return howManyTimes;
198         }
199 
AppendRegisterToDictionary(SVDRegister register)200         private void AppendRegisterToDictionary(SVDRegister register)
201         {
202             var bytes = register.SizeInBytes;
203             for(var i = 0u; i < bytes; i++)
204             {
205                 var address = register.Address + i;
206                 if(!registerDictionary.ContainsKey(address))
207                 {
208                     registerDictionary.Add(address, register);
209                 }
210                 else
211                 {
212                     // There is a posibility to set the same address for various registers.
213                     // e.g. first register with a read-only permission access but second with a write-only.
214                     registerDictionary[address].MergeWithRegister(register);
215                 }
216             }
217         }
218 
LogReadSuccess(ulong value, string peripheralName, string name, ulong offset, SysbusAccessWidth width, ulong? originalOffset = null)219         private void LogReadSuccess(ulong value, string peripheralName, string name, ulong offset, SysbusAccessWidth width, ulong? originalOffset = null)
220         {
221             var formatString = "Read{5} from an unimplemented register {1}:{2} (0x{3:X}){4}, returning a value from SVD: 0x{0:X}";
222             formatString = currentSystemBus.DecorateWithCPUNameAndPC(formatString);
223 
224             var originalReadIndication = String.Empty;
225             if(originalOffset.HasValue)
226             {
227                 originalReadIndication = $" (caused by reading offset 0x{originalOffset:X})";
228             }
229             currentSystemBus.Log(
230                 LogLevel.Warning,
231                 formatString,
232                 value,
233                 peripheralName,
234                 name,
235                 offset,
236                 originalReadIndication,
237                 width
238             );
239         }
240 
LogWriteSuccess(ulong value, string peripheralName, string name, ulong offset, SysbusAccessWidth width, bool writeOnce = false, ulong? originalOffset = null, ulong? originalValue = null)241         private void LogWriteSuccess(ulong value, string peripheralName, string name, ulong offset, SysbusAccessWidth width, bool writeOnce = false, ulong? originalOffset = null, ulong? originalValue = null)
242         {
243             var formatString = "Write{6} of value 0x{0:X} to an unimplemented{5} register {1}:{2} (0x{3:X}){4} generated from SVD";
244             formatString = currentSystemBus.DecorateWithCPUNameAndPC(formatString);
245 
246             var originalWriteIndication = String.Empty;
247             var writeOnceIndication = String.Empty;
248             if(originalValue.HasValue)
249             {
250                 originalWriteIndication = $" (caused by writing offset 0x{originalOffset:X} value 0x{originalValue:X})";
251             }
252             if(writeOnce)
253             {
254                 writeOnceIndication = " write-once";
255             }
256             currentSystemBus.Log(
257                 LogLevel.Warning,
258                 formatString,
259                 value,
260                 peripheralName,
261                 name,
262                 offset,
263                 originalWriteIndication,
264                 writeOnceIndication,
265                 width
266             );
267         }
268 
LogReadFail(string peripheralName, string name, ulong offset, SysbusAccessWidth width, ulong? originalOffset = null)269         private void LogReadFail(string peripheralName, string name, ulong offset, SysbusAccessWidth width, ulong? originalOffset = null)
270         {
271             var formatString = "Invalid Read{4} from an unimplemented write-only register {0}:{1} (0x{2:X}){3}, returning a value from SVD: 0x0";
272             formatString = currentSystemBus.DecorateWithCPUNameAndPC(formatString);
273 
274             var originalReadIndication = String.Empty;
275             if(originalOffset.HasValue)
276             {
277                 originalReadIndication = $" (caused by reading offset 0x{originalOffset:X})";
278             }
279             currentSystemBus.Log(
280                 LogLevel.Warning,
281                 formatString,
282                 peripheralName,
283                 name,
284                 offset,
285                 originalReadIndication,
286                 width
287             );
288         }
289 
LogWriteFail(ulong value, string peripheralName, string name, ulong offset, SysbusAccessWidth width, ulong? originalOffset = null, ulong? originalValue = null)290         private void LogWriteFail(ulong value, string peripheralName, string name, ulong offset, SysbusAccessWidth width, ulong? originalOffset = null, ulong? originalValue = null)
291         {
292             var formatString = "Invalid Write{5} of value 0x{0:X} to an unimplemented read-only register {1}:{2} (0x{3:X}){4} generated from SVD";
293             formatString = currentSystemBus.DecorateWithCPUNameAndPC(formatString);
294 
295             var originalWriteIndication = String.Empty;
296             if(originalValue.HasValue)
297             {
298                 originalWriteIndication = $" (caused by writing offset 0x{originalOffset:X} value 0x{originalValue:X})";
299             }
300             currentSystemBus.Log(
301                 LogLevel.Warning,
302                 formatString,
303                 value,
304                 peripheralName,
305                 name,
306                 offset,
307                 originalWriteIndication,
308                 width
309             );
310         }
311 
CheckAndGetWidth(SysbusAccessWidth type)312         private static int CheckAndGetWidth(SysbusAccessWidth type)
313         {
314             switch(type)
315             {
316             case SysbusAccessWidth.Byte:
317             case SysbusAccessWidth.Word:
318             case SysbusAccessWidth.DoubleWord:
319             case SysbusAccessWidth.QuadWord:
320                 return (int)type;
321             default:
322                 throw new ArgumentException($"'{type}' is unsupported width of data.");
323             }
324         }
325 
CalculateOffset(ulong? baseAddress, ulong? addressOffset)326         private static ulong? CalculateOffset(ulong? baseAddress, ulong? addressOffset)
327         {
328             if(baseAddress == null)
329             {
330                 return null;
331             }
332             else
333             {
334                 if(addressOffset == null)
335                 {
336                     return baseAddress;
337                 }
338                 else
339                 {
340                     return baseAddress + addressOffset;
341                 }
342             }
343         }
344 
GetMandatoryField(XElement node, string fieldName)345         private static string GetMandatoryField(XElement node, string fieldName)
346         {
347             var fieldElement = node.Element(fieldName);
348             if(fieldElement != null)
349             {
350                 return fieldElement.Value;
351             }
352             else
353             {
354                 var path = GetPath(node);
355                 throw new RecoverableException($"Field '{fieldName}' in '{path}' is required.");
356             }
357         }
358 
GetOptionalFieldOrNull(XElement node, string fieldName)359         private static string GetOptionalFieldOrNull(XElement node, string fieldName)
360         {
361             var fieldElement = node.Element(fieldName);
362             if(fieldElement != null)
363             {
364                 return fieldElement.Value;
365             }
366             else
367             {
368                 return null;
369             }
370         }
371 
GetPath(XElement node)372         private static string GetPath(XElement node)
373         {
374             var rootElementName = "device";
375             var path = node.Element("name") != null ? node.Element("name").Value : $"<{node.Name.LocalName}>";
376             var tmpElement = node.Parent;
377             if(tmpElement != null)
378             {
379                 while(tmpElement.Name != rootElementName)
380                 {
381                     var tmpNameElement = tmpElement.Element("name");
382                     if(tmpNameElement != null)
383                     {
384                         path = $"{tmpNameElement.Value}.{path}";
385                     }
386                     tmpElement = tmpElement.Parent;
387                 }
388             }
389             return path;
390         }
391 
SmartParseHexOrDecimal(string whatToParse, XElement node)392         private static ulong? SmartParseHexOrDecimal(string whatToParse, XElement node)
393         {
394             if(whatToParse == null)
395             {
396                 return null;
397             }
398             else
399             {
400                 SmartParser.Instance.TryParse(whatToParse, typeof(ulong), out var result);
401                 if(result != null)
402                 {
403                     return (ulong)result;
404                 }
405                 else
406                 {
407                     throw new RecoverableException($"Cannot parse '{whatToParse}' to a number in '{GetPath(node)}'.");
408                 }
409             }
410         }
411 
412         private IBusController currentSystemBus;
413         private Dictionary<ulong, SVDRegister> registerDictionary;
414 
415         private class SVDDevice
416         {
SVDDevice(XElement deviceNode, SVDParser parent)417             public SVDDevice(XElement deviceNode, SVDParser parent)
418             {
419                 this.deviceNode = deviceNode;
420                 Parent = parent;
421                 Description = GetMandatoryField(deviceNode, "description");
422                 Name = GetMandatoryField(deviceNode, "name");
423                 Peripherals = new List<SVDPeripheral>();
424 
425                 var cpuElement = deviceNode.Element("cpu");
426                 if(cpuElement == null)
427                 {
428                     Endianess = Endianess.LittleEndian;
429                 }
430                 else
431                 {
432                     var endianessString = GetMandatoryField(cpuElement, "endian");
433                     Endianess = GetEndianess(endianessString);
434                 }
435 
436                 var defaultRegisterSettings = GetLocalRegisterSettings(deviceNode);
437                 ScanPeripherals(defaultRegisterSettings);
438             }
439 
440             public String Name { get; private set; }
441             public String Description { get; private set; }
442             public Endianess Endianess { get; private set; }
443             public List<SVDPeripheral> Peripherals { get; private set; }
444             public SVDParser Parent { get; private set; }
445 
ScanPeripherals(RegisterSettings defaultRegisterSettings)446             private void ScanPeripherals(RegisterSettings defaultRegisterSettings)
447             {
448                 if(deviceNode.Element("peripherals") == null)
449                 {
450                     throw new RecoverableException("There is no <peripherals> section.");
451                 }
452                 var peripheralElements = deviceNode.Element("peripherals").Elements("peripheral");
453                 if(!peripheralElements.Any())
454                 {
455                     Parent.currentSystemBus.Log(LogLevel.Warning, "There are no <peripheral> sections in <peripherals>.");
456                 }
457                 foreach(var peripheralElement in peripheralElements)
458                 {
459                     var derivingAttribute = peripheralElement.Attribute("derivedFrom");
460                     if(derivingAttribute == null)
461                     {
462                         ScanPeripheral(peripheralElement, defaultRegisterSettings);
463                     }
464                     else
465                     {
466                         var derivedElement = GetDerivedElementFromTheScope(
467                             peripheralElement,
468                             peripheralElements,
469                             NestingType.Peripheral,
470                             derivingAttribute.Value
471                         );
472                         if(!peripheralElement.Elements("registers").Any())
473                         {
474                             peripheralElement.Add(derivedElement.Element("registers"));
475                         }
476                         var newDefaultRegisterSettings = GetRegisterSettings(derivedElement, defaultRegisterSettings);
477                         ScanPeripheral(peripheralElement, newDefaultRegisterSettings);
478                     }
479                 }
480             }
481 
ScanPeripheral(XElement node, RegisterSettings defaultRegisterSettings)482             private void ScanPeripheral(XElement node, RegisterSettings defaultRegisterSettings)
483             {
484                 var name = GetMandatoryField(node, "name");
485                 var newRegisterSettings = GetRegisterSettings(node, defaultRegisterSettings);
486                 var peripheral = new SVDPeripheral(name, this);
487                 Peripherals.Add(peripheral);
488 
489                 var registersElement = node.Element("registers");
490                 if(registersElement != null)
491                 {
492                     ScanRegistersAndClusters(registersElement, newRegisterSettings, peripheral);
493                 }
494             }
495 
ScanRegistersAndClusters(XElement node, RegisterSettings defaultRegisterSettings, SVDPeripheral peripheral)496             private void ScanRegistersAndClusters(XElement node, RegisterSettings defaultRegisterSettings, SVDPeripheral peripheral)
497             {
498                 ScanClusters(node, defaultRegisterSettings, peripheral);
499                 ScanRegisters(node, defaultRegisterSettings, peripheral);
500             }
501 
GetDerivedElementFromTheScope(XElement element, IEnumerable<XElement> collection, NestingType type, string derivedName)502             private XElement GetDerivedElementFromTheScope(XElement element, IEnumerable<XElement> collection, NestingType type, string derivedName)
503             {
504                 var result = collection.FirstOrDefault(x => x.Name == type.ToString().ToLower() && ElementNameEquals(x, derivedName));
505                 if(result == null)
506                 {
507                     var name = GetMandatoryField(element, "name");
508                     throw new RecoverableException($"The SVD {type} '{name}' derives from '{derivedName}', but '{derivedName}' cannot be found.");
509                 }
510                 return result;
511             }
512 
ScanClusters(XElement node, RegisterSettings defaultRegisterSettings, SVDPeripheral peripheral)513             private void ScanClusters(XElement node, RegisterSettings defaultRegisterSettings, SVDPeripheral peripheral)
514             {
515                 var clusterItems = node.Elements("cluster");
516                 foreach(var cluster in clusterItems)
517                 {
518                     var derivedAttribute = cluster.Attribute("derivedFrom");
519                     if(derivedAttribute == null)
520                     {
521                         ScanCluster(cluster, defaultRegisterSettings, peripheral);
522                     }
523                     else
524                     {
525                         var derivedClusterName = derivedAttribute.Value;
526                         var derivedCluster = FindDerivedElement(NestingType.Cluster, cluster, derivedAttribute.Value, defaultRegisterSettings, out var outRegisterSettings);
527 
528                         if(!cluster.Elements("register").Any())
529                         {
530                             cluster.Add(derivedCluster.Elements("register"));
531                         }
532                         if(!cluster.Elements("cluster").Any())
533                         {
534                             cluster.Add(derivedCluster.Elements("cluster"));
535                         }
536 
537                         ScanCluster(cluster, outRegisterSettings, peripheral);
538                     }
539                 }
540             }
541 
ScanRegisters(XElement node, RegisterSettings defaultRegisterSettings, SVDPeripheral peripheral)542             private void ScanRegisters(XElement node, RegisterSettings defaultRegisterSettings, SVDPeripheral peripheral)
543             {
544                 var registerItems = node.Elements("register");
545                 foreach(var register in registerItems)
546                 {
547                     var derivedAttribute = register.Attribute("derivedFrom");
548                     if(derivedAttribute == null)
549                     {
550                         ScanRegister(register, defaultRegisterSettings, peripheral);
551                     }
552                     else
553                     {
554                         var derivedRegisterName = derivedAttribute.Value;
555                         var derivedRegister = FindDerivedElement(NestingType.Register, register, derivedAttribute.Value, defaultRegisterSettings, out var outRegisterSettings);
556 
557                         ScanRegister(register, outRegisterSettings, peripheral);
558                     }
559                 }
560             }
561 
FindDerivedElement(NestingType nestingType, XElement element, String derivedElementName, RegisterSettings defaultRegisterSettings, out RegisterSettings outRegisterSettings)562             private XElement FindDerivedElement(NestingType nestingType, XElement element, String derivedElementName, RegisterSettings defaultRegisterSettings, out RegisterSettings outRegisterSettings)
563             {
564                 string[] derivedSplitNode = derivedElementName.Split('.');
565                 outRegisterSettings = defaultRegisterSettings;
566 
567                 XElement derivedElement;
568                 if(derivedSplitNode.Length == 1)
569                 {
570                     derivedElement = GetDerivedElementFromTheScope(
571                         element,
572                         element.Parent.Elements(),
573                         nestingType,
574                         derivedElementName
575                     );
576                 }
577                 else
578                 {
579                     var derivedPeripheral = GetDerivedElementFromTheScope(
580                         element,
581                         deviceNode.Element("peripherals").Elements(),
582                         NestingType.Peripheral,
583                         derivedSplitNode[0]
584                     );
585 
586                     outRegisterSettings = GetRegisterSettings(derivedPeripheral, defaultRegisterSettings);
587                     derivedElement = derivedPeripheral.Element("registers");
588 
589                     // if we are deriving from the cluster we should go through all cluster in the cluster chain,
590                     // but if we are deriving from register we should go through all cluster chain but last one.
591                     var limit = derivedSplitNode.Length - (nestingType == NestingType.Register ? 1 : 0);
592                     for(var i = 1; i < limit; i++)
593                     {
594                         derivedElement = GetDerivedElementFromTheScope(
595                             derivedElement,
596                             derivedElement.Elements("cluster"),
597                             NestingType.Cluster,
598                             derivedSplitNode[i]
599                         );
600                         var address = CalculateOffset(
601                             outRegisterSettings.Address,
602                             GetAddressOffset(derivedElement)
603                         );
604                         outRegisterSettings = GetRegisterSettings(derivedElement, outRegisterSettings, address);
605                     }
606                     // If we are deriving from the register, we must find this register in the last cluster in the chain.
607                     if(nestingType == NestingType.Register)
608                     {
609                         derivedElement = derivedElement.Elements("register").First(x => x.Element("name").Value == derivedSplitNode[derivedSplitNode.Length - 1]);
610                     }
611                 }
612                 outRegisterSettings = GetRegisterSettings(derivedElement, outRegisterSettings, defaultRegisterSettings.Address);
613                 return derivedElement;
614             }
615 
ScanCluster(XElement node, RegisterSettings defaultRegisterSettings, SVDPeripheral peripheral)616             private void ScanCluster(XElement node, RegisterSettings defaultRegisterSettings, SVDPeripheral peripheral)
617             {
618                 GetMandatoryField(node, "name");
619                 var address = CalculateOffset(
620                     defaultRegisterSettings.Address,
621                     GetAddressOffset(node)
622                 );
623                 var newRegisterSettings = GetRegisterSettings(node, defaultRegisterSettings, address);
624                 ScanRegistersAndClusters(node, newRegisterSettings, peripheral);
625             }
626 
ScanRegister(XElement node, RegisterSettings defaultRegisterSettings, SVDPeripheral peripheral)627             private void ScanRegister(XElement node, RegisterSettings defaultRegisterSettings, SVDPeripheral peripheral)
628             {
629                 var address = CalculateOffset(
630                     defaultRegisterSettings.Address,
631                     GetAddressOffset(node)
632                 );
633                 var newRegisterSettings = GetRegisterSettings(node, defaultRegisterSettings, address);
634                 var name = GetMandatoryField(node, "name");
635                 var newRegister = new SVDRegister(node, name, newRegisterSettings, peripheral);
636                 peripheral.Registers.Add(newRegister);
637             }
638 
GetLocalRegisterSettings(XElement node)639             private static RegisterSettings GetLocalRegisterSettings(XElement node)
640             {
641                 var sizeString = GetOptionalFieldOrNull(node, "size");
642                 var resetValueString = GetOptionalFieldOrNull(node, "resetValue");
643                 var accessString = GetOptionalFieldOrNull(node, "access");
644                 ulong? address = null;
645                 var nodeName = node.Name.LocalName;
646                 if(nodeName == NestingType.Peripheral.ToString().ToLower())
647                 {
648                     address = GetBaseAddress(node);
649                 }
650                 else if(nodeName == NestingType.Cluster.ToString().ToLower() || nodeName == NestingType.Register.ToString().ToLower())
651                 {
652                     address = GetAddressOffset(node);
653                 }
654 
655                 return new RegisterSettings(
656                     (int?)SmartParseHexOrDecimal(sizeString, node),
657                     (ulong?)SmartParseHexOrDecimal(resetValueString, node),
658                     address,
659                     GetPermittedAccess(accessString)
660                 );
661             }
662 
GetRegisterSettings(XElement node, RegisterSettings defaultRegisterSettings, ulong? definiteAddress = null)663             private static RegisterSettings GetRegisterSettings(XElement node, RegisterSettings defaultRegisterSettings, ulong? definiteAddress = null)
664             {
665                 var localRegisterSettings = GetLocalRegisterSettings(node);
666                 return new RegisterSettings(
667                     defaultRegisterSettings,
668                     localRegisterSettings.Size,
669                     localRegisterSettings.ResetValue,
670                     definiteAddress ?? localRegisterSettings.Address,
671                     localRegisterSettings.Access
672                 );
673             }
674 
GetBaseAddress(XElement node)675             private static ulong? GetBaseAddress(XElement node)
676             {
677                 var addressOffsetString = GetMandatoryField(node, "baseAddress");
678                 return SmartParseHexOrDecimal(addressOffsetString, node);
679             }
680 
GetAddressOffset(XElement node)681             private static ulong? GetAddressOffset(XElement node)
682             {
683                 var addressOffsetString = GetMandatoryField(node, "addressOffset");
684                 return SmartParseHexOrDecimal(addressOffsetString, node);
685             }
686 
GetPermittedAccess(string accessString)687             private static PermittedAccess? GetPermittedAccess(string accessString)
688             {
689                 switch(accessString?.ToLower())
690                 {
691                 case null:
692                     return null;
693                 case "read-only":
694                     return PermittedAccess.Read;
695                 case "write-only":
696                     return PermittedAccess.Write;
697                 case "read-write":
698                     return PermittedAccess.Read | PermittedAccess.Write;
699                 case "writeonce":
700                     return PermittedAccess.WriteOnce;
701                 case "read-writeonce":
702                     return PermittedAccess.Read | PermittedAccess.WriteOnce;
703                 default:
704                     throw new RecoverableException(string.Format("Found element with unexpected access type: {0}.", accessString));
705                 }
706             }
707 
GetEndianess(string endianessString)708             private static Endianess GetEndianess(string endianessString)
709             {
710                 switch(endianessString)
711                 {
712                 case "little":
713                     return Endianess.LittleEndian;
714                 case "big":
715                     return Endianess.BigEndian;
716                 case "selectable":
717                     return Endianess.Selectable;
718                 case "other":
719                     return Endianess.Other;
720                 default:
721                     throw new RecoverableException(string.Format("Found element with unexpected endianess type: {0}.", endianessString));
722                 }
723             }
724 
ElementNameEquals(XElement node, string name)725             private static bool ElementNameEquals(XElement node, string name)
726             {
727                 var nameElement = node.Elements("name").FirstOrDefault();
728                 if(nameElement != null && nameElement.Value == name)
729                 {
730                     return true;
731                 }
732                 return false;
733             }
734 
735             private XElement deviceNode;
736         }
737 
738         private class SVDPeripheral
739         {
SVDPeripheral(string name, SVDDevice includedDevice)740             public SVDPeripheral(string name, SVDDevice includedDevice)
741             {
742                 Name = name;
743                 Registers = new List<SVDRegister>();
744                 ParentDevice = includedDevice;
745             }
746 
747             public string Name { get; private set; }
748             public List<SVDRegister> Registers { get; private set; }
749             public SVDDevice ParentDevice { get; private set; }
750         }
751 
752         private class SVDRegister
753         {
SVDRegister(XElement node, string name, RegisterSettings settings, SVDPeripheral peripheral)754             public SVDRegister(XElement node, string name, RegisterSettings settings, SVDPeripheral peripheral)
755             {
756                 Name = name;
757                 path = GetPath(node);
758                 Peripheral = peripheral;
759                 Size = settings.Size ?? throw new RecoverableException($"Size not provided for register '{path}'.");
760                 // Register's size can be not dividable by 8
761                 SizeInBytes = (uint)Math.Ceiling(Size / 8.0);
762                 // Reset value assumed to be 0 if not provided. Maximum size of the register cropped to 64 bits.
763                 ResetValue = (settings.ResetValue & 0xFFFFFFFFFFFFFFFFul) ?? 0u;
764                 if(Size > 64)
765                 {
766                     ResetValue = 0u;
767                     Peripheral.ParentDevice.Parent.currentSystemBus.Log(
768                         LogLevel.Warning,
769                         "Register {0} size set to {1} bits. Registers larger than 64 bits are not supported. Reset value for this register is set to {2}.",
770                         Name,
771                         Size,
772                         ResetValue
773                     );
774                 }
775                 Address = settings.Address ?? throw new RecoverableException($"Address not provided for register '{path}'.");
776                 Access = settings.Access ?? PermittedAccess.Read | PermittedAccess.Write;
777             }
778 
779             public bool HasReadAccess => (Access & PermittedAccess.Read) != 0;
780             public bool HasWriteAccess => (Access & PermittedAccess.Write) != 0;
781             public bool HasWriteOnceAccess => (Access & PermittedAccess.WriteOnce) != 0;
782 
783             public string Name { get; private set; }
784             public int Size { get; private set; }
785             public ulong Address { get; private set; }
786             public uint SizeInBytes { get; private set; }
787             public PermittedAccess Access { get; private set; }
788             public SVDPeripheral Peripheral { get; private set; }
789 
790             public ulong ResetValue
791             {
792                 get
793                 {
794                     return resetValueWithCorrectEndianess;
795                 }
796                 private set
797                 {
798                     var resetValueInLittleEndian = value;
799                     // In SVD file it is possible to try set larger value than registers capacity is.
800                     var max = (ulong)(1L << Size) - 1;
801                     if(resetValueInLittleEndian > max)
802                     {
803                         var normalizeResetValue = resetValueInLittleEndian & max;
804                         Peripheral.ParentDevice.Parent.currentSystemBus.Log(
805                             LogLevel.Warning,
806                             "The reset value 0x{0:X} does not fit to {1}-bit register '{2}'. Reset value set to 0x{3:X}.",
807                             resetValueInLittleEndian,
808                             Size,
809                             path,
810                             normalizeResetValue
811                         );
812                         resetValueInLittleEndian = normalizeResetValue;
813                     }
814 
815                     if(Peripheral.ParentDevice.Endianess == Endianess.BigEndian)
816                     {
817                         var resetValueInBigEndian = 0ul;
818                         for(var i = 0; i < SizeInBytes; i++)
819                         {
820                             var tmp = ((resetValueInLittleEndian >> 8 * i) & 0xFF);
821                             resetValueInBigEndian += tmp << (int)(8 * (SizeInBytes - 1 - i));
822                         }
823                         resetValueWithCorrectEndianess = resetValueInBigEndian;
824                     }
825                     else
826                     {
827                         resetValueWithCorrectEndianess = resetValueInLittleEndian;
828                     }
829                 }
830             }
831 
MergeWithRegister(SVDRegister register)832             public void MergeWithRegister(SVDRegister register)
833             {
834                 Name = Name + "|" + register.Name;
835                 Access |= register.Access;
836                 if(!HasReadAccess && register.HasReadAccess)
837                 {
838                     ResetValue = register.ResetValue;
839                 }
840             }
841 
842             private ulong resetValueWithCorrectEndianess;
843             private string path;
844         }
845 
846         private struct RegisterSettings
847         {
RegisterSettingsAntmicro.Renode.Peripherals.Bus.SVDParser.RegisterSettings848             public RegisterSettings(int? size, ulong? resetValue, ulong? address, PermittedAccess? access)
849             {
850                 Size = size;
851                 ResetValue = resetValue;
852                 Address = address;
853                 Access = access;
854             }
855 
RegisterSettingsAntmicro.Renode.Peripherals.Bus.SVDParser.RegisterSettings856             public RegisterSettings(RegisterSettings parentRegisterSettings, int? size = null, ulong? resetValue = null, ulong? address = null, PermittedAccess? access = null)
857             {
858                 Size = size ?? parentRegisterSettings.Size;
859                 ResetValue = resetValue ?? parentRegisterSettings.ResetValue;
860                 Address = address ?? parentRegisterSettings.Address;
861                 Access = access ?? parentRegisterSettings.Access;
862             }
863 
864             public int? Size { get; private set; }
865             public ulong? ResetValue { get; private set; }
866             public ulong? Address { get; private set; }
867             public PermittedAccess? Access { get; private set; }
868         }
869 
870         [Flags]
871         private enum PermittedAccess
872         {
873             Read = 1,
874             Write = 2,
875             WriteOnce = 4
876         }
877 
878         private enum NestingType
879         {
880             Peripheral,
881             Cluster,
882             Register
883         }
884 
885         private enum Endianess
886         {
887             LittleEndian,
888             BigEndian,
889             Selectable,
890             Other
891         }
892 
893         private enum OperationDirection
894         {
895             Read,
896             Write
897         }
898     }
899 }
900