1 //
2 // Copyright (c) 2010-2025 Antmicro
3 // Copyright (c) 2011-2015 Realtime Embedded
4 //
5 // This file is licensed under the MIT License.
6 // Full license text is available in 'licenses/MIT.txt'.
7 //
8 using System;
9 using System.Linq;
10 using Antmicro.Renode.Core;
11 using Antmicro.Renode.Debugging;
12 using Antmicro.Renode.Logging;
13 using Antmicro.Renode.Utilities.Binding;
14 using System.Collections.Generic;
15 using Antmicro.Renode.Peripherals.Bus;
16 using Antmicro.Renode.Peripherals.UART;
17 using Antmicro.Renode.Core.Structure;
18 using Antmicro.Renode.Exceptions;
19 using Antmicro.Renode.Utilities;
20 using Antmicro.Renode.Peripherals.Miscellaneous;
21 using Endianess = ELFSharp.ELF.Endianess;
22 
23 namespace Antmicro.Renode.Peripherals.CPU
24 {
25     [GPIO(NumberOfInputs = 2)]
26     public abstract partial class Arm : TranslationCPU, ICPUWithHooks, IPeripheralRegister<SemihostingUart, NullRegistrationPoint>, IPeripheralRegister<ArmPerformanceMonitoringUnit, NullRegistrationPoint>
27     {
Arm(string cpuType, IMachine machine, uint cpuId = 0, Endianess endianness = Endianess.LittleEndian, uint? numberOfMPURegions = null, ArmSignalsUnit signalsUnit = null)28         public Arm(string cpuType, IMachine machine, uint cpuId = 0, Endianess endianness = Endianess.LittleEndian, uint? numberOfMPURegions = null, ArmSignalsUnit signalsUnit = null)
29             : base(cpuId, cpuType, machine, endianness)
30         {
31             if(numberOfMPURegions.HasValue)
32             {
33                 this.NumberOfMPURegions = numberOfMPURegions.Value;
34             }
35 
36             if(signalsUnit != null)
37             {
38                 // There's no such unit in hardware but we need to share certain signals between cores.
39                 this.signalsUnit = signalsUnit;
40                 signalsUnit.RegisterCPU(this);
41             }
42         }
43 
Register(SemihostingUart peripheral, NullRegistrationPoint registrationPoint)44         public void Register(SemihostingUart peripheral, NullRegistrationPoint registrationPoint)
45         {
46             if(semihostingUart != null)
47             {
48                 throw new RegistrationException("A semihosting uart is already registered.");
49             }
50             semihostingUart = peripheral;
51             machine.RegisterAsAChildOf(this, peripheral, registrationPoint);
52         }
53 
Unregister(SemihostingUart peripheral)54         public void Unregister(SemihostingUart peripheral)
55         {
56             semihostingUart = null;
57             machine.UnregisterAsAChildOf(this, peripheral);
58         }
59 
Register(ArmPerformanceMonitoringUnit peripheral, NullRegistrationPoint registrationPoint)60         public void Register(ArmPerformanceMonitoringUnit peripheral, NullRegistrationPoint registrationPoint)
61         {
62             if(performanceMonitoringUnit != null)
63             {
64                 throw new RegistrationException("A PMU is already registered.");
65             }
66             performanceMonitoringUnit = peripheral;
67             machine.RegisterAsAChildOf(this, peripheral, registrationPoint);
68 
69             performanceMonitoringUnit.RegisterCPU(this);
70         }
71 
Unregister(ArmPerformanceMonitoringUnit peripheral)72         public void Unregister(ArmPerformanceMonitoringUnit peripheral)
73         {
74             performanceMonitoringUnit = null;
75             machine.UnregisterAsAChildOf(this, peripheral);
76         }
77 
78         public override string Architecture { get { return "arm"; } }
79 
80         //gdb does not contain arm-m and armv7 as independent architecteures so we need to pass "arm" in every case.
81         public override string GDBArchitecture { get { return "arm"; } }
82 
83         public override List<GDBFeatureDescriptor> GDBFeatures { get { return new List<GDBFeatureDescriptor>(); } }
84 
85         public bool ImplementsPMSA => MemorySystemArchitecture == MemorySystemArchitectureType.Physical_PMSA;
86         public bool ImplementsVMSA => MemorySystemArchitecture == MemorySystemArchitectureType.Virtual_VMSA;
87         public abstract MemorySystemArchitectureType MemorySystemArchitecture { get; }
88 
89         public virtual uint ExceptionVectorAddress
90         {
91             get => TlibGetExceptionVectorAddress();
92             set
93             {
94                 // It's "arm-m" for CortexM.
95                 DebugHelper.Assert(Architecture == "arm");
96 
97                 if(ExceptionVectorAddress == value)
98                 {
99                     return;
100                 }
101                 TlibSetExceptionVectorAddress(value);
102 
103                 // On HW, in Arm CPUs, such a change is only possible in:
104                 // * ARMv6K and ARMv7-A CPUs with Security Extensions using VBAR/MVBAR,
105                 // * ARMv8-A and ARMv8-R CPUs using VBAR_EL{1..3},
106                 // * Cortex-M CPUs using VTOR and
107                 // * pre-ARMv8 CPUs using VINITHI signal to use Hivecs which uses 0xFFFF_0000.
108                 //
109                 // Cortex-M overrides this property so let's only make sure it isn't used there.
110                 // ARMv8-A and ARMv8-R are handled by unrelated ARMv8A and ARMv8R classes.
111                 //
112                 // Let's allow this customization for all the remaining Arm CPUs with info log
113                 // when changing to value other than 0x0 and 0xFFFF_0000 that it might not be
114                 // supported on hardware.
115                 var cpuSupportsVBAR = IsSystemRegisterAccessible("VBAR", isWrite: false);
116                 const uint hivecsVectorAddress = 0xFFFF0000u;
117                 if(!cpuSupportsVBAR && value != 0x0 && value != hivecsVectorAddress)
118                 {
119                     this.Log(LogLevel.Info,
120                             "Successfully set {0} to 0x{1:X} on a CPU supporting neither VBAR nor VTOR; "
121                             + "such customization might not be possible on hardware.",
122                             nameof(ExceptionVectorAddress), value
123                     );
124                 }
125             }
126         }
127 
128         public uint ModelID
129         {
130             get
131             {
132                 return TlibGetCpuModelId();
133             }
134             set
135             {
136                 TlibSetCpuModelId(value);
137             }
138         }
139 
140         public bool WfiAsNop
141         {
142             get => wfiAsNop;
143             set
144             {
145                 wfiAsNop = value;
146                 neverWaitForInterrupt = wfiAsNop && wfeAndSevAsNop;
147             }
148         }
149 
150         public bool WfeAndSevAsNop
151         {
152             get => wfeAndSevAsNop;
153             set
154             {
155                 wfeAndSevAsNop = value;
156                 neverWaitForInterrupt = wfiAsNop && wfeAndSevAsNop;
157             }
158         }
159 
160         public uint NumberOfMPURegions
161         {
162             get
163             {
164                 return TlibGetNumberOfMpuRegions();
165             }
166             set
167             {
168                 TlibSetNumberOfMpuRegions(value);
169             }
170         }
171 
172         protected bool wfiAsNop;
173         protected bool wfeAndSevAsNop;
174 
175         [Export]
Read32CP15(uint instruction)176         protected uint Read32CP15(uint instruction)
177         {
178             return Read32CP15Inner(new Coprocessor32BitMoveInstruction(instruction));
179         }
180 
181         [Export]
Write32CP15(uint instruction, uint value)182         protected void Write32CP15(uint instruction, uint value)
183         {
184             Write32CP15Inner(new Coprocessor32BitMoveInstruction(instruction), value);
185         }
186 
187         [Export]
Read64CP15(uint instruction)188         protected ulong Read64CP15(uint instruction)
189         {
190             return Read64CP15Inner(new Coprocessor64BitMoveInstruction(instruction));
191         }
192 
193         [Export]
Write64CP15(uint instruction, ulong value)194         protected void Write64CP15(uint instruction, ulong value)
195         {
196             Write64CP15Inner(new Coprocessor64BitMoveInstruction(instruction), value);
197         }
198 
DecodeInterrupt(int number)199         protected override Interrupt DecodeInterrupt(int number)
200         {
201             switch(number)
202             {
203                 case 0:
204                     return Interrupt.Hard;
205                 case 1:
206                     return Interrupt.TargetExternal1;
207                 default:
208                     throw InvalidInterruptNumberException;
209             }
210         }
211 
Read32CP15Inner(Coprocessor32BitMoveInstruction instruction)212         protected virtual uint Read32CP15Inner(Coprocessor32BitMoveInstruction instruction)
213         {
214             if(instruction.Opc1 == 4 && instruction.Opc2 == 0 && instruction.CRm == 0 && instruction.CRn == 15) // CBAR
215             {
216                 // SCU's offset from CBAR is 0x0 so let's just return its address.
217                 var scusRegistered = machine.SystemBus.Children.Where(registered => registered.Peripheral is ArmSnoopControlUnit);
218                 switch(scusRegistered.Count())
219                 {
220                     case 0:
221                         this.Log(LogLevel.Warning, "Tried to establish CBAR from SCU address but found no SCU registered for this CPU, returning 0x0.");
222                         return 0;
223                     case 1:
224                         return checked((uint)scusRegistered.Single().RegistrationPoint.StartingPoint);
225                     default:
226                         this.Log(LogLevel.Error, "Tried to establish CBAR from SCU address but found more than one SCU. Aborting.");
227                         throw new CpuAbortException();
228                 }
229             }
230             this.Log(LogLevel.Warning, "Unknown CP15 32-bit read - {0}, returning 0x0", instruction);
231             return 0;
232         }
233 
Write32CP15Inner(Coprocessor32BitMoveInstruction instruction, uint value)234         protected virtual void Write32CP15Inner(Coprocessor32BitMoveInstruction instruction, uint value)
235         {
236             this.Log(LogLevel.Warning, "Unknown CP15 32-bit write - {0}", instruction);
237         }
238 
Read64CP15Inner(Coprocessor64BitMoveInstruction instruction)239         protected virtual ulong Read64CP15Inner(Coprocessor64BitMoveInstruction instruction)
240         {
241             this.Log(LogLevel.Warning, "Unknown CP15 64-bit read - {0}, returning 0x0", instruction);
242             return 0;
243         }
244 
Write64CP15Inner(Coprocessor64BitMoveInstruction instruction, ulong value)245         protected virtual void Write64CP15Inner(Coprocessor64BitMoveInstruction instruction, ulong value)
246         {
247             this.Log(LogLevel.Warning, "Unknown CP15 64-bit write - {0}", instruction);
248         }
249 
BeforePCWrite(UInt32 value)250         protected virtual UInt32 BeforePCWrite(UInt32 value)
251         {
252             TlibSetThumb((int)(value & 0x1));
253             return value & ~(uint)0x1;
254         }
255 
GetItState()256         public uint GetItState()
257         {
258             uint itState = TlibGetItState();
259             if((itState & 0x1F) == 0)
260             {
261                 this.Log(LogLevel.Warning, "Checking IT_STATE, while not in IT block");
262             }
263             return itState;
264         }
265 
WillNextItInstructionExecute(uint itState)266         public bool WillNextItInstructionExecute(uint itState)
267         {
268             /* Returns true if the oldest bit of 'abcd' field is set to 0 and the condition is met.
269              * If there is no trailing one in the lower part, we are not in an IT block*/
270             var MaskBit = (itState & 0x10) == 0 && ((itState & 0xF) > 0);
271             var condition = (itState >> 4) & 0x0E;
272             if(EvaluateConditionCode(condition))
273             {
274                 return MaskBit;
275             }
276             else
277             {
278                 return !MaskBit;
279             }
280         }
281 
EvaluateConditionCode(uint condition)282         public bool EvaluateConditionCode(uint condition)
283         {
284             return TlibEvaluateConditionCode(condition) > 0;
285         }
286 
GetExceptionDescription(ulong exceptionIndex)287         protected override string GetExceptionDescription(ulong exceptionIndex)
288         {
289             if(exceptionIndex >= (ulong)ExceptionDescriptions.Length)
290             {
291                 return base.GetExceptionDescription(exceptionIndex);
292             }
293 
294             return ExceptionDescriptions[exceptionIndex];
295         }
296 
Reset()297         public override void Reset()
298         {
299             base.Reset();
300             foreach(var config in defaultTCMConfiguration)
301             {
302                 RegisterTCMRegion(config);
303             }
304         }
305 
SetEventFlag(bool value)306         public void SetEventFlag(bool value)
307         {
308             TlibSetEventFlag(value ? 1 : 0);
309         }
310 
SetSevOnPending(bool value)311         public void SetSevOnPending(bool value)
312         {
313             TlibSetSevOnPending(value ? 1 : 0);
314         }
315 
RegisterTCMRegion(IMemory memory, uint interfaceIndex, uint regionIndex)316         public void RegisterTCMRegion(IMemory memory, uint interfaceIndex, uint regionIndex)
317         {
318             Action<IMachine, MachineStateChangedEventArgs> hook = null;
319             hook = (_, args) =>
320             {
321                 if(args.CurrentState != MachineStateChangedEventArgs.State.Started)
322                 {
323                     return;
324                 }
325                 if(!TryRegisterTCMRegion(memory, interfaceIndex, regionIndex))
326                 {
327                     this.Log(LogLevel.Error, "Attempted to register a TCM #{0} region #{1}, but {2} is not registered for this cpu.", interfaceIndex, regionIndex, machine.GetLocalName(memory));
328                 }
329                 machine.StateChanged -= hook;
330             };
331             machine.StateChanged += hook;
332         }
333 
RegisterTCMRegion(TCMConfiguration config)334         private void RegisterTCMRegion(TCMConfiguration config)
335         {
336             try
337             {
338                 TlibRegisterTcmRegion(config.Address, config.Size, ((ulong)config.InterfaceIndex << 32) | config.RegionIndex);
339             }
340             catch(Exception e)
341             {
342                 throw new RecoverableException(e);
343             }
344         }
345 
GetSystemRegisterValue(string name)346         public ulong GetSystemRegisterValue(string name)
347         {
348             ValidateSystemRegisterAccess(name, isWrite: false);
349 
350             return TlibGetSystemRegister(name, 1u /* log_unhandled_access: true */);
351         }
352 
SetSystemRegisterValue(string name, ulong value)353         public void SetSystemRegisterValue(string name, ulong value)
354         {
355             ValidateSystemRegisterAccess(name, isWrite: true);
356 
357             TlibSetSystemRegister(name, value, 1u /* log_unhandled_access: true */);
358         }
359 
IsSystemRegisterAccessible(string name, bool isWrite)360         private bool IsSystemRegisterAccessible(string name, bool isWrite)
361         {
362             var result = TlibCheckSystemRegisterAccess(name, isWrite ? 1u : 0u);
363             return (SystemRegisterCheckReturnValue)result == SystemRegisterCheckReturnValue.AccessValid;
364         }
365 
ValidateSystemRegisterAccess(string name, bool isWrite)366         private void ValidateSystemRegisterAccess(string name, bool isWrite)
367         {
368             switch((SystemRegisterCheckReturnValue)TlibCheckSystemRegisterAccess(name, isWrite ? 1u : 0u))
369             {
370             case SystemRegisterCheckReturnValue.AccessValid:
371                 return;
372             case SystemRegisterCheckReturnValue.AccessorNotFound:
373                 var accessName = isWrite ? "Writing" : "Reading";
374                 throw new RecoverableException($"{accessName} the {name} register isn't supported.");
375             case SystemRegisterCheckReturnValue.RegisterNotFound:
376                 throw new RecoverableException("No such register.");
377             default:
378                 throw new ArgumentException("Invalid TlibCheckSystemRegisterAccess return value!");
379             }
380         }
381 
TryRegisterTCMRegion(IMemory memory, uint interfaceIndex, uint regionIndex)382         private bool TryRegisterTCMRegion(IMemory memory, uint interfaceIndex, uint regionIndex)
383         {
384             ulong address;
385             if(!TCMConfiguration.TryFindRegistrationAddress((SystemBus)machine.SystemBus, this, memory, out address))
386             {
387                 return false;
388             }
389 
390             var config = new TCMConfiguration(checked((uint)address), checked((ulong)memory.Size), regionIndex, interfaceIndex);
391             RegisterTCMRegion(config);
392             defaultTCMConfiguration.Add(config);
393 
394             return true;
395         }
396 
397         [Export]
ReportPMUOverflow(int counter)398         private void ReportPMUOverflow(int counter)
399         {
400             performanceMonitoringUnit?.OnOverflowAction(counter);
401         }
402 
403         private ArmPerformanceMonitoringUnit performanceMonitoringUnit;
404 
405         [Export]
DoSemihosting()406         private uint DoSemihosting()
407         {
408             var uart = semihostingUart;
409             //this.Log(LogLevel.Error, "Semihosing, r0={0:X}, r1={1:X} ({2:X})", this.GetRegister(0), this.GetRegister(1), this.TranslateAddress(this.GetRegister(1)));
410 
411             uint operation = R[0];
412             uint r1 = R[1];
413             uint result = 0;
414             switch(operation)
415             {
416                 case 7: // SYS_READC
417                     if(uart == null) break;
418                     result = uart.SemihostingGetByte();
419                     break;
420                 case 3: // SYS_WRITEC
421                 case 4: // SYS_WRITE0
422                     if(uart == null) break;
423                     string s = "";
424                     var addr = this.TranslateAddress(r1, MpuAccess.InstructionFetch);
425                     do
426                     {
427                         var c = this.Bus.ReadByte(addr++);
428                         if(c == 0) break;
429                         s = s + Convert.ToChar(c);
430                         if((operation) == 3) break; // SYS_WRITEC
431                     } while(true);
432                     uart.SemihostingWriteString(s);
433                     break;
434                 default:
435                     this.Log(LogLevel.Debug, "Unknown semihosting operation: 0x{0:X}", operation);
436                     break;
437             }
438             return result;
439         }
440 
441         [Export]
FillConfigurationSignalsState(IntPtr allocatedStatePointer)442         private void FillConfigurationSignalsState(IntPtr allocatedStatePointer)
443         {
444             // It's OK not to set the fields if there's no ArmConfigurationSignals.
445             // Default values of structure's fields are neutral to the simulation.
446             signalsUnit?.FillConfigurationStateStruct(allocatedStatePointer, this);
447         }
448 
449         [Export]
IsWfiAsNop()450         private uint IsWfiAsNop()
451         {
452             return WfiAsNop ? 1u : 0u;
453         }
454 
455         [Export]
IsWfeAndSevAsNop()456         private uint IsWfeAndSevAsNop()
457         {
458             return WfeAndSevAsNop ? 1u : 0u;
459         }
460 
461         private SemihostingUart semihostingUart = null;
462 
463         [Export]
SetSystemEvent(int value)464         private void SetSystemEvent(int value)
465         {
466             var flag = value != 0;
467 
468             foreach(var cpu in machine.SystemBus.GetCPUs().OfType<Arm>())
469             {
470                 cpu.SetEventFlag(flag);
471             }
472         }
473 
474         public enum MemorySystemArchitectureType
475         {
476             None,
477             Physical_PMSA,
478             Virtual_VMSA,
479         }
480 
481         private enum SystemRegisterCheckReturnValue
482         {
483             RegisterNotFound = 1,
484             AccessorNotFound = 2,
485             AccessValid = 3,
486         }
487 
488         private readonly List<TCMConfiguration> defaultTCMConfiguration = new List<TCMConfiguration>();
489         private readonly ArmSignalsUnit signalsUnit;
490 
491         // 649:  Field '...' is never assigned to, and will always have its default value null
492 #pragma warning disable 649
493 
494         [Import]
495         private Action<uint> TlibSetCpuModelId;
496 
497         [Import]
498         private Func<uint> TlibGetItState;
499 
500         [Import]
501         private Func<uint, uint> TlibEvaluateConditionCode;
502 
503         [Import]
504         private Func<uint> TlibGetCpuModelId;
505 
506         [Import]
507         private Action<int> TlibSetThumb;
508 
509         [Import]
510         private Action<int> TlibSetEventFlag;
511 
512         [Import]
513         private Action<int> TlibSetSevOnPending;
514 
515         [Import]
516         private Action<uint> TlibSetNumberOfMpuRegions;
517 
518         [Import]
519         private Func<uint> TlibGetNumberOfMpuRegions;
520 
521         [Import]
522         private Action<uint, ulong, ulong> TlibRegisterTcmRegion;
523 
524         [Import]
525         private Func<string, uint, uint> TlibCheckSystemRegisterAccess;
526 
527         [Import]
528         // The arguments are: char *name, bool log_unhandled_access.
529         private Func<string, uint, ulong> TlibGetSystemRegister;
530 
531         [Import]
532         // The arguments are: char *name, uint64_t value, bool log_unhandled_access.
533         private Action<string, ulong, uint> TlibSetSystemRegister;
534 
535         [Import]
536         public Action<int, uint> TlibUpdatePmuCounters;
537 
538         [Import]
539         public Action<uint> TlibPmuSetDebug;
540 
541         [Import]
542         public Func<uint> TlibGetExceptionVectorAddress;
543 
544         [Import]
545         public Action<uint> TlibSetExceptionVectorAddress;
546 
547 #pragma warning restore 649
548 
549         private readonly string[] ExceptionDescriptions =
550         {
551             "Undefined instruction",
552             "Software interrupt",
553             "Instruction Fetch Memory Abort (Prefetch Abort)",
554             "Data Access Memory Abort (Data Abort)",
555             "Normal Interrupt (IRQ)",
556             "Fast Interrupt (FIQ)",
557             "Breakpoint",
558             "Kernel Trap",
559             "STREX instruction"
560         };
561 
562         protected struct Coprocessor32BitMoveInstruction
563         {
operator ==Antmicro.Renode.Peripherals.CPU.Arm.Coprocessor32BitMoveInstruction564             public static bool operator ==(Coprocessor32BitMoveInstruction a, Coprocessor32BitMoveInstruction b)
565             {
566                 return a.FieldsOnly == b.FieldsOnly;
567             }
568 
operator !=Antmicro.Renode.Peripherals.CPU.Arm.Coprocessor32BitMoveInstruction569             public static bool operator !=(Coprocessor32BitMoveInstruction a, Coprocessor32BitMoveInstruction b)
570             {
571                 return !(a == b);
572             }
573 
Coprocessor32BitMoveInstructionAntmicro.Renode.Peripherals.CPU.Arm.Coprocessor32BitMoveInstruction574             public Coprocessor32BitMoveInstruction(uint instruction)
575             {
576                 Opc1 = BitHelper.GetValue(instruction, Opc1Offset, Opc1Size);
577                 CRn = BitHelper.GetValue(instruction, CRnOffset, CRnSize);
578                 Opc2 = BitHelper.GetValue(instruction, Opc2Offset, Opc2Size);
579                 CRm = BitHelper.GetValue(instruction, CRmOffset, CRmSize);
580                 FieldsOnly = instruction & FieldsMask;
581             }
582 
Coprocessor32BitMoveInstructionAntmicro.Renode.Peripherals.CPU.Arm.Coprocessor32BitMoveInstruction583             public Coprocessor32BitMoveInstruction(uint opc1, uint crn, uint crm, uint opc2)
584             {
585                 Opc1 = opc1;
586                 CRn = crn;
587                 CRm = crm;
588                 Opc2 = opc2;
589                 FieldsOnly = (Opc1 << Opc1Offset) | (CRn << CRnOffset) | (CRm << CRmOffset) | (Opc2 << Opc2Offset);
590             }
591 
EqualsAntmicro.Renode.Peripherals.CPU.Arm.Coprocessor32BitMoveInstruction592             public override bool Equals(object o)
593             {
594                 return o is Coprocessor32BitMoveInstruction b && this == b;
595             }
596 
GetHashCodeAntmicro.Renode.Peripherals.CPU.Arm.Coprocessor32BitMoveInstruction597             public override int GetHashCode()
598             {
599                 return (int)FieldsOnly;
600             }
601 
ToStringAntmicro.Renode.Peripherals.CPU.Arm.Coprocessor32BitMoveInstruction602             public override string ToString()
603             {
604                 return $"op1={Opc1}, crn={CRn}, crm={CRm}, op2={Opc2}";
605             }
606 
607             public uint Opc1 { get; }
608             public uint CRn { get; }
609             public uint Opc2 { get; }
610             public uint CRm { get; }
611             public uint FieldsOnly { get; }
612 
613             public static readonly uint FieldsMask = BitHelper.CalculateMask(Opc1Size, Opc1Offset) | BitHelper.CalculateMask(CRnSize, CRnOffset)
614                 | BitHelper.CalculateMask(Opc2Size, Opc2Offset) | BitHelper.CalculateMask(CRmSize, CRmOffset);
615 
616             private const int Opc1Size = 3;
617             private const int CRnSize = 4;
618             private const int Opc2Size = 3;
619             private const int CRmSize = 4;
620 
621             private const int Opc1Offset = 21;
622             private const int CRnOffset = 16;
623             private const int Opc2Offset = 5;
624             private const int CRmOffset = 0;
625         }
626 
627         protected struct Coprocessor64BitMoveInstruction
628         {
Coprocessor64BitMoveInstructionAntmicro.Renode.Peripherals.CPU.Arm.Coprocessor64BitMoveInstruction629             public Coprocessor64BitMoveInstruction(uint instruction)
630             {
631                 Opc1 = BitHelper.GetValue(instruction, Opc1Offset, Opc1Size);
632                 CRm = BitHelper.GetValue(instruction, CRmOffset, CRmSize);
633                 FieldsOnly = instruction & FieldsMask;
634             }
635 
ToStringAntmicro.Renode.Peripherals.CPU.Arm.Coprocessor64BitMoveInstruction636             public override string ToString()
637             {
638                 return $"op1={Opc1}, crm={CRm}";
639             }
640 
641             public uint Opc1 { get; }
642             public uint CRm { get; }
643             public uint FieldsOnly { get; }
644 
645             public static readonly uint FieldsMask = BitHelper.CalculateMask(Opc1Size, Opc1Offset) | BitHelper.CalculateMask(CRmSize, CRmOffset);
646 
647             private const int Opc1Size = 4;
648             private const int CRmSize = 4;
649 
650             private const int Opc1Offset = 4;
651             private const int CRmOffset = 0;
652         }
653     }
654 }
655