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