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.Collections.Generic; 9 using System.Linq; 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Core.Structure.Registers; 12 using Antmicro.Renode.Exceptions; 13 using Antmicro.Renode.Logging; 14 using Antmicro.Renode.Peripherals.CPU; 15 using Antmicro.Renode.Utilities; 16 17 namespace Antmicro.Renode.Peripherals.Miscellaneous 18 { 19 public class ArmPerformanceMonitoringUnit : BasicDoubleWordPeripheral, IKnownSize 20 { VerifyCPUType(Arm cpu)21 public static void VerifyCPUType(Arm cpu) 22 { 23 if(!SupportedCPUTypes.Any(t => t.IsAssignableFrom(cpu.GetType()))) 24 { 25 throw new RecoverableException( 26 $"Tried to register {nameof(ArmPerformanceMonitoringUnit)} at {cpu.GetType()} while it can only be currently used with CPUs assignable from:" 27 + SupportedCPUTypes.Select(t => "\n* " + t.ToString()) 28 ); 29 } 30 } 31 32 // PMU is implemented in tlib, currently it isn't supported by ARMv8A/R tlib. 33 public static readonly Type[] SupportedCPUTypes = new[] { typeof(ARMv7A), typeof(ARMv7R) }; 34 35 // PMU logic is implemented in the CPU itself 36 // This block only exposes PMU interrupt and optional configuration interface ArmPerformanceMonitoringUnit(IMachine machine, ulong peripheralId = PeripheralIdDefault, bool withProcessorIdMMIORegisters = true)37 public ArmPerformanceMonitoringUnit(IMachine machine, ulong peripheralId = PeripheralIdDefault, bool withProcessorIdMMIORegisters = true) : base(machine) 38 { 39 // CoreSight's PeripheralID contains core-specific fields, let's allow precising them in REPLs. 40 PeripheralId = peripheralId; 41 42 // It's unsure whether MMIO registers should include processor ID registers which is 43 // why it's configurable. See the comment over `ProcessorIdRegisters`. 44 this.withProcessorIdMMIORegisters = withProcessorIdMMIORegisters; 45 46 IRQ = new GPIO(); 47 48 DefineMemoryMappedRegisters(); 49 } 50 Reset()51 public override void Reset() 52 { 53 base.Reset(); 54 55 // Most of the properties and fields keep the configuration and don't need to be reset. 56 IRQ.Unset(); 57 SoftwareLockEnabled = true; 58 59 // Nothing more to do here, PMU will reset itself when CPU resets 60 } 61 WriteDoubleWord(long offset, uint value)62 public override void WriteDoubleWord(long offset, uint value) 63 { 64 // Only the `LockAccess` register is accessible with Software Lock enabled. 65 if(offset != (long)Registers.LockAccess && SoftwareLockEnabled) 66 { 67 this.Log(LogLevel.Warning, "Tried to write PMU register other than PMLAR using MMIO interface with Software Lock enabled, write ignored"); 68 this.Log(LogLevel.Info, "Software Lock can be cleared by writing 0x{0:X} to the PMLAR register at 0x{1:X3}", SoftwareLockDisableValue, (ulong)Registers.LockAccess); 69 return; 70 } 71 72 base.WriteDoubleWord(offset, value); 73 } 74 OnOverflowAction(int counter)75 public void OnOverflowAction(int counter) 76 { 77 this.DebugLog("PMU reporting counter overflow for counter {0}", counter); 78 IRQ.Set(true); 79 } 80 RegisterCPU(Arm cpu)81 public void RegisterCPU(Arm cpu) 82 { 83 VerifyCPUType(cpu); 84 parentCPU = cpu; 85 86 if(withProcessorIdMMIORegisters) 87 { 88 DefineProcessorIdRegisters(cpu); 89 } 90 91 // Peripheral ID's bits 20-23 (4-7 of `PMPID2`) contain CPU's major revision number, 92 // AKA variant, which is also encoded in bits 20-23 of MIDR. Let's warn if those differ 93 // and update Peripheral ID's variant field to MIDR variant's value. 94 var midrVariant = BitHelper.GetValue(cpu.GetSystemRegisterValue("MIDR"), VariantOffset, VariantWidth); 95 var peripheralIdVariant = BitHelper.GetValue(PeripheralId, VariantOffset, VariantWidth); 96 if(midrVariant != peripheralIdVariant) 97 { 98 PeripheralId = BitHelper.ReplaceBits(PeripheralId, midrVariant, VariantWidth, destinationPosition: VariantOffset); 99 this.Log(LogLevel.Info, "Updating Peripheral ID's CPU variant ({0}) in bits 20-23 to the actual CPU variant from MIDR ({1}).", peripheralIdVariant, midrVariant); 100 } 101 } 102 103 public GPIO IRQ { get; } 104 105 public long Size => 0x1000; 106 107 // This PMU doesn't support 64-bit registers GetRegister(string register)108 public uint GetRegister(string register) 109 { 110 VerifyCPURegistered(); 111 VerifyRegister(register); 112 return (uint)parentCPU.GetSystemRegisterValue(register); 113 } 114 SetRegister(string register, uint value)115 public void SetRegister(string register, uint value) 116 { 117 VerifyCPURegistered(); 118 VerifyRegister(register); 119 parentCPU.SetSystemRegisterValue(register, value); 120 } 121 122 // Convenience wrapper for getting an individual counter value GetCounterValue(uint counter)123 public uint GetCounterValue(uint counter) 124 { 125 ValidateCounter(counter); 126 127 uint res = 0; 128 // We are manipulating internal PMU state, we need to pause emulation 129 using(parentCPU.GetMachine().ObtainPausedState(true)) 130 { 131 var selectedCounter = (uint)parentCPU.GetSystemRegisterValue(SelectedCounterRegister); 132 parentCPU.SetSystemRegisterValue(SelectedCounterRegister, counter); 133 res = (uint)parentCPU.GetSystemRegisterValue(CounterValueRegister); 134 // Restore selected counter 135 parentCPU.SetSystemRegisterValue(SelectedCounterRegister, selectedCounter); 136 } 137 return res; 138 } 139 140 // Convenience wrapper for setting an individual counter value SetCounterValue(uint counter, uint value)141 public void SetCounterValue(uint counter, uint value) 142 { 143 ValidateCounter(counter); 144 // We are manipulating internal PMU state, we need to pause emulation 145 using(parentCPU.GetMachine().ObtainPausedState(true)) 146 { 147 var selectedCounter = (uint)parentCPU.GetSystemRegisterValue(SelectedCounterRegister); 148 parentCPU.SetSystemRegisterValue(SelectedCounterRegister, counter); 149 parentCPU.SetSystemRegisterValue(CounterValueRegister, value); 150 // Restore selected counter 151 parentCPU.SetSystemRegisterValue(SelectedCounterRegister, selectedCounter); 152 } 153 } 154 155 // Convenience wrapper for binding an event to a counter SetCounterEvent(uint counter, uint @event, bool ignoreCountAtPL0 = false, bool ignoreCountAtPL1 = false)156 public void SetCounterEvent(uint counter, uint @event, bool ignoreCountAtPL0 = false, bool ignoreCountAtPL1 = false) 157 { 158 ValidateCounter(counter); 159 // We are manipulating internal PMU state, we need to pause emulation 160 using(parentCPU.GetMachine().ObtainPausedState(true)) 161 { 162 var selectedCounter = (uint)parentCPU.GetSystemRegisterValue(SelectedCounterRegister); 163 parentCPU.SetSystemRegisterValue(SelectedCounterRegister, counter); 164 165 uint eventRegisterValue = @event & CounterEventRegisterEventMask; 166 eventRegisterValue |= (ignoreCountAtPL0 ? 1u : 0u) << CounterEventRegisterPL0CountIgnoreOffset; 167 eventRegisterValue |= (ignoreCountAtPL1 ? 1u : 0u) << CounterEventRegisterPL1CountIgnoreOffset; 168 parentCPU.SetSystemRegisterValue(CounterEventRegister, eventRegisterValue); 169 170 // Restore selected counter 171 parentCPU.SetSystemRegisterValue(SelectedCounterRegister, selectedCounter); 172 } 173 } 174 175 // Convenience wrapper for getting the event bound to a counter GetCounterEvent(uint counter)176 public uint GetCounterEvent(uint counter) 177 { 178 ValidateCounter(counter); 179 180 uint res = 0; 181 // We are manipulating internal PMU state, we need to pause emulation 182 using(parentCPU.GetMachine().ObtainPausedState(true)) 183 { 184 var selectedCounter = (uint)parentCPU.GetSystemRegisterValue(SelectedCounterRegister); 185 parentCPU.SetSystemRegisterValue(SelectedCounterRegister, counter); 186 res = (uint)parentCPU.GetSystemRegisterValue(CounterEventRegister); 187 // Restore selected counter 188 parentCPU.SetSystemRegisterValue(SelectedCounterRegister, selectedCounter); 189 } 190 return res; 191 } 192 GetCycleCounterValue()193 public uint GetCycleCounterValue() 194 { 195 VerifyCPURegistered(); 196 197 return (uint)parentCPU.GetSystemRegisterValue("PMCCNTR"); 198 } 199 200 // Artificially increase the value of all event counters subscribing to `eventId` by `value` 201 // Can trigger overflow interrupt if it is enabled BroadcastEvent(int eventId, uint value)202 public void BroadcastEvent(int eventId, uint value) 203 { 204 VerifyCPURegistered(); 205 if(!Enabled) 206 { 207 throw new RecoverableException("PMU is disabled, this operation won't execute"); 208 } 209 210 parentCPU.TlibUpdatePmuCounters(eventId, value); 211 } 212 213 // Enable additional debug logs in the PMU 214 // To see the logs, it is also needed to set CPU logLevel to DEBUG (logLevel 0) Debug(bool status)215 public void Debug(bool status) 216 { 217 VerifyCPURegistered(); 218 219 this.Log(LogLevel.Info, "If you want to see PMU logs, remember to set DEBUG or lower logLevel on {0}", parentCPU.GetName()); 220 parentCPU.TlibPmuSetDebug((uint)(status ? 1 : 0)); 221 } 222 223 public bool Enabled 224 { 225 get 226 { 227 VerifyCPURegistered(); 228 return (parentCPU.GetSystemRegisterValue(ControlRegister) & ControlRegisterEnableMask) > 0; 229 } 230 set 231 { 232 VerifyCPURegistered(); 233 var register = parentCPU.GetSystemRegisterValue(ControlRegister) & ~ControlRegisterEnableMask; 234 parentCPU.SetSystemRegisterValue(ControlRegister, (value ? 1u : 0u) | register); 235 } 236 } 237 238 // Combined value from `PMCID` registers. 239 public uint ComponentId { get; set; } = ComponentIdDefault; 240 241 // Zero by default; the value is IMPLEMENTATION DEFAULT according to the ARMv7AR manual. 242 public uint DeviceConfiguration { get; set; } 243 244 // The property only influences a value in the `PMCFGR` Configuration Register. 245 // It doesn't make the X flag in the `PMCR` register, i.e. Export Enable, RAZ cause 246 // the flag has no effects whatsoever (see `pmu.c:set_c9_pmcr` in tlib). 247 public bool ExportSupported { get; set; } 248 249 // Inverted "Software Lock", writes can be enabled by writing `SoftwareLockDisableValue` 250 // to the `LockAccess` register. 251 public bool SoftwareLockEnabled { get; set; } = true; 252 253 // Each of the `PMPID` registers has just 1 byte of the "64-bit conceptual Peripheral ID". 254 // Therefore the LSB of this value will provide the LSB of `PMPID0`, byte1 will provide the 255 // LSB of `PMPID1` etc; the same as shown for `DBGPID` in the ARMv7AR manual's Figure C11-1 256 // "Mapping between Debug Peripheral ID Registers and a 64-bit Peripheral ID value". 257 public ulong PeripheralId { get; private set; } 258 259 // Has no real influence on any logic. 260 public bool SecureNonInvasiveDebugEnabled { get; set; } 261 262 // Based on `PMCID` values from the ARMv7AR manual. 263 public const uint ComponentIdDefault = 0xB105900D; 264 265 // "64-bit conceptual Peripheral ID" with `PMPID` values from the ARMv7AR manual. 266 public const ulong PeripheralIdDefault = 0x04000BB000; 267 DefineRegisterWithSingleIdByte(DoubleWordRegister register, int index, Func<ulong> valueProvider, string name)268 private static void DefineRegisterWithSingleIdByte(DoubleWordRegister register, int index, Func<ulong> valueProvider, string name) 269 { 270 if(valueProvider == null) 271 { 272 throw new ArgumentNullException(nameof(valueProvider)); 273 } 274 275 register 276 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => valueProvider().AsRawBytes()[index], name: $"{name} byte {index}") 277 .WithReservedBits(8, 24) 278 ; 279 } 280 VerifyRegister(string register)281 private static void VerifyRegister(string register) 282 { 283 if(!ImplementedRegisters.Contains(register.ToUpperInvariant())) 284 { 285 throw new RecoverableException($"Invalid register: {register}. See \"ImplementedRegisters\" property for the list of registers"); 286 } 287 } 288 DefineMemoryMappedRegisters()289 private void DefineMemoryMappedRegisters() 290 { 291 Registers.EventCount0.DefineMany(this, EventCountersCount, DefineEventCountRegister); 292 Registers.EventTypeSelect0.DefineMany(this, EventCountersCount, DefineEventTypeSelectRegister); 293 294 DefineRegisterAccessingSystemRegister(Registers.CommonEventIdentification0, "PMCEID0"); 295 DefineRegisterAccessingSystemRegister(Registers.CommonEventIdentification1, "PMCEID1"); 296 DefineRegisterAccessingSystemRegister(Registers.Control, "PMCR"); 297 DefineRegisterAccessingSystemRegister(Registers.CountEnableClear, "PMCNTENCLR"); 298 DefineRegisterAccessingSystemRegister(Registers.CountEnableSet, "PMCNTENSET"); 299 DefineRegisterAccessingSystemRegister(Registers.CycleCount, "PMCCNTR", FieldMode.Read); 300 DefineRegisterAccessingSystemRegister(Registers.InterruptEnableClear, "PMINTENCLR"); 301 DefineRegisterAccessingSystemRegister(Registers.InterruptEnableSet, "PMINTENSET"); 302 DefineRegisterAccessingSystemRegister(Registers.OverflowFlagStatus, "PMOVSR"); 303 // DefineRegisterAccessingSystemRegister(Registers.OverflowFlagStatusSet, "PMOVSSET"); // Not supported in tlib. 304 DefineRegisterAccessingSystemRegister(Registers.SoftwareIncrement, "PMSWINC", FieldMode.Write); 305 DefineRegisterAccessingSystemRegister(Registers.UserEnable, "PMUSERENR"); 306 307 Registers.Configuration.Define(this) 308 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => EventCountersCount, name: "Number of event counters (N)") 309 .WithEnumField<DoubleWordRegister, CounterSizes>(8, 6, FieldMode.Read, valueProviderCallback: _ => CounterSizes.Counters32Bit, name: "Counter size (SIZE)") 310 .WithFlag(14, FieldMode.Read, valueProviderCallback: _ => true, name: "Cycle counter implemented (CC)") 311 .WithFlag(15, FieldMode.Read, valueProviderCallback: _ => true, name: "Cycle counter clock divider implemented (CCD)") 312 .WithFlag(16, writeCallback: (_, newValue) => ExportSupported = newValue, valueProviderCallback: _ => ExportSupported, name: "Export supported (EX)") 313 .WithReservedBits(17, 2) 314 .WithFlag(19, FieldMode.Read, valueProviderCallback: _ => true, name: "User-mode EnableRegister implemented (UEN)") 315 .WithReservedBits(20, 12); 316 317 // `PMCLAIM*` registers aren't described in the ARMv7AR manual; their implementation is based on the similar `DBGCLAIM*` registers. 318 Registers.ClaimTagClear.Define(this) 319 // `CLAIM` bits aren't used in the model besides the `ClaimTagSet` below. 320 .WithFlags(0, 8, out var claimBits, FieldMode.Read | FieldMode.WriteOneToClear) 321 .WithReservedBits(8, 24); 322 323 Registers.ClaimTagSet.Define(this) 324 // This register is only read to find out which bits are supported. `ClaimTagClear` can only be used to really read `claimBits`. 325 .WithFlags(0, 8, writeCallback: (index, _, newValue) => claimBits[index].Value |= newValue, valueProviderCallback: (_, __) => true) 326 .WithReservedBits(8, 24); 327 328 Registers.LockAccess.Define(this) 329 .WithValueField(0, 32, FieldMode.Write, writeCallback: (_, newValue) => 330 { 331 var newSoftwareLockEnabled = newValue != SoftwareLockDisableValue; 332 if(SoftwareLockEnabled == newSoftwareLockEnabled) 333 { 334 if(SoftwareLockEnabled) 335 { 336 this.Log(LogLevel.Warning, "Tried to disable Software Lock with invalid value 0x{0:X}, should be 0x{1:X}", newValue, SoftwareLockDisableValue); 337 } 338 return; 339 } 340 341 this.Log(LogLevel.Debug, "Software Lock {0}", newSoftwareLockEnabled ? "enabled" : "disabled"); 342 SoftwareLockEnabled = newSoftwareLockEnabled; 343 } 344 ); 345 346 Registers.LockStatus.Define(this) 347 .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => true, name: "Software Lock implemented (SLI)") 348 .WithFlag(1, FieldMode.Read, valueProviderCallback: _ => SoftwareLockEnabled, name: "Software Lock status (SLK)") 349 .WithFlag(2, FieldMode.Read, valueProviderCallback: _ => false, name: "Not 32-bit (nTT)") 350 .WithReservedBits(3, 29); 351 352 // Based on `PMAUTHSTATUS` for implementations without the Security Extensions; don't confuse it with `DBGAUTHSTATUS`. 353 Registers.AuthenticationStatus.Define(this) 354 .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => false, name: "Non-secure invasive debug enabled (NSE)") 355 .WithFlag(1, FieldMode.Read, valueProviderCallback: _ => false, name: "Non-secure invasive debug features implemented (NSI)") 356 .WithFlag(2, FieldMode.Read, valueProviderCallback: _ => false, name: "Non-secure non-invasive debug enabled (NSNE)") 357 .WithFlag(3, FieldMode.Read, valueProviderCallback: _ => false, name: "Non-secure non-invasive debug features implemented (NSNI)") 358 .WithFlag(4, FieldMode.Read, valueProviderCallback: _ => false, name: "Secure invasive debug enabled (SE)") 359 .WithFlag(5, FieldMode.Read, valueProviderCallback: _ => false, name: "Secure invasive debug features implemented (SI)") 360 // The `SNE` flag is the only one which isn't constant according to the ARMv7AR manual hence the public settable property. 361 // Its value should be "the logical result of `DBGEN` OR `NIDEN`" signals though. 362 .WithFlag(6, FieldMode.Read, valueProviderCallback: _ => SecureNonInvasiveDebugEnabled, name: "Secure non-invasive debug enabled (SNE)") 363 .WithFlag(7, FieldMode.Read, valueProviderCallback: _ => true, name: "Secure non-invasive debug features implemented (SNI)") 364 .WithReservedBits(8, 24); 365 366 Registers.DeviceConfiguration.Define(this) 367 .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => DeviceConfiguration); 368 369 Registers.DeviceType.Define(this) 370 .WithValueField(0, 4, FieldMode.Read, valueProviderCallback: _ => 0x6, name: "Main class (C)") // Performance Monitors 371 .WithValueField(4, 4, FieldMode.Read, valueProviderCallback: _ => 0x1, name: "Sub type (T)") // Processor 372 .WithReservedBits(8, 24); 373 374 // We start with PeripheralIdentification4 because registers 4-7 are before 0-3; hence also the unusual index manipulation. 375 Registers.PeripheralIdentification4.DefineMany(this, 8, 376 (register, index) => DefineRegisterWithSingleIdByte(register, index < 4 ? index + 4 : index - 4, () => PeripheralId, "Peripheral ID") 377 ); 378 379 Registers.ComponentIdentification0.DefineMany(this, 4, 380 (register, index) => DefineRegisterWithSingleIdByte(register, index, () => ComponentId, "Component ID") 381 ); 382 } 383 DefineEventCountRegister(DoubleWordRegister eventCountRegister, int index)384 private void DefineEventCountRegister(DoubleWordRegister eventCountRegister, int index) 385 { 386 var counterIndex = (uint)index; 387 eventCountRegister.DefineValueField(0, 32, 388 writeCallback: (_, newValue) => SetCounterValue(counterIndex, (uint)newValue), 389 valueProviderCallback: _ => GetCounterValue(counterIndex) 390 ); 391 } 392 DefineEventTypeSelectRegister(DoubleWordRegister eventTypeSelectRegister, int index)393 private void DefineEventTypeSelectRegister(DoubleWordRegister eventTypeSelectRegister, int index) 394 { 395 var counter = (uint)index; 396 eventTypeSelectRegister 397 .WithValueField(0, 8, 398 writeCallback: (_, newValue) => SetCounterEvent(counter, (uint)newValue), 399 valueProviderCallback: _ => GetCounterEvent(counter)) 400 .WithReservedBits(8, 24); 401 } 402 DefineProcessorIdRegisters(Arm cpu)403 private void DefineProcessorIdRegisters(Arm cpu) 404 { 405 var midrAliases = new List<ProcessorIdRegisters> 406 { 407 ProcessorIdRegisters.MainIDAlias, 408 // It's a `MIDR` alias if `REVIDR` isn't really implemented which is true for tlib. 409 // The manual says it's UNKNOWN otherwise as `REVIDR` can only be read via CP15. 410 ProcessorIdRegisters.RevisionID_REVIDR, 411 }; 412 midrAliases.AddIf(!cpu.ImplementsPMSA, ProcessorIdRegisters.MPUType_MPUIR); 413 midrAliases.AddIf(!cpu.ImplementsVMSA, ProcessorIdRegisters.TLBType_TLBTR); 414 415 foreach(var register in Enum.GetValues(typeof(ProcessorIdRegisters)).Cast<ProcessorIdRegisters>()) 416 { 417 // Except for MIDR aliases, the registers name is included in the `register` name. 418 var systemRegisterName = midrAliases.Contains(register) ? "MIDR" : register.ToString().Split(new[] {'_'}, count: 2).Last(); 419 register.Define(this) 420 .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => parentCPU.GetSystemRegisterValue(systemRegisterName)); 421 } 422 } 423 DefineRegisterAccessingSystemRegister(Registers register, string systemRegisterName, FieldMode fieldMode = FieldMode.Read | FieldMode.Write)424 private void DefineRegisterAccessingSystemRegister(Registers register, string systemRegisterName, FieldMode fieldMode = FieldMode.Read | FieldMode.Write) 425 { 426 register.Define(this) 427 .WithValueField(0, 32, fieldMode, 428 writeCallback: fieldMode.IsWritable() ? (_, newValue) => parentCPU.SetSystemRegisterValue(systemRegisterName, newValue) : (Action<ulong, ulong>)null, 429 valueProviderCallback: fieldMode.IsReadable() ? _ => parentCPU.GetSystemRegisterValue(systemRegisterName) : (Func<ulong, ulong>)null 430 ); 431 } 432 ValidateCounter(uint counter)433 private void ValidateCounter(uint counter) 434 { 435 VerifyCPURegistered(); 436 437 var pmcr = (uint)parentCPU.GetSystemRegisterValue(ControlRegister); 438 var supportedCounters = BitHelper.GetMaskedValue(pmcr, 11, 5) >> 11; 439 440 if(counter >= supportedCounters) 441 { 442 throw new RecoverableException($"Invalid counter: {counter}, select from 0 to {supportedCounters - 1}"); 443 } 444 } 445 VerifyCPURegistered()446 private void VerifyCPURegistered() 447 { 448 if(parentCPU == null) 449 { 450 throw new RecoverableException("No CPU registered itself in the PMU. You can register it manually by calling \"RegisterCPU\""); 451 } 452 } 453 454 // This list needs to be maintained in sync with the tlib implementation of PMU 455 // E.g. if we extend PMU implementation beyond this registers, they need to be added here too 456 // It's made public so the list is available in the Monitor for the end user 457 public static readonly HashSet<string> ImplementedRegisters 458 = new HashSet<string> 459 { 460 "PMCR", 461 "PMCNTENSET", 462 "PMCNTENCLR", 463 "PMOVSR", 464 "PMSWINC", 465 466 "PMCEID0", 467 "PMCEID1", 468 469 "PMSELR", 470 "PMCCNTR", 471 "PMXEVTYPER", 472 "PMXEVCNTR", 473 474 "PMUSERENR", 475 "PMINTENSET", 476 "PMINTENCLR", 477 }; 478 479 private Arm parentCPU; 480 481 private readonly bool withProcessorIdMMIORegisters; 482 483 private const string SelectedCounterRegister = "PMSELR"; 484 private const string CounterValueRegister = "PMXEVCNTR"; 485 private const string CounterEventRegister = "PMXEVTYPER"; 486 private const string ControlRegister = "PMCR"; 487 488 private const uint CounterEventRegisterEventMask = 0xFF; 489 private const int CounterEventRegisterPL0CountIgnoreOffset = 30; 490 private const int CounterEventRegisterPL1CountIgnoreOffset = 31; 491 private const uint ControlRegisterEnableMask = 0x1; 492 private const uint EventCountersCount = 31; // Doesn't include cycle counter. 493 private const uint SoftwareLockDisableValue = 0xC5ACCE55; 494 private const int VariantOffset = 20; // In both Peripheral ID and MIDR. 495 private const int VariantWidth = 4; // In both Peripheral ID and MIDR. 496 497 private enum CounterSizes 498 { 499 Counters32Bit = 0b011111, 500 Counters64Bit = 0b111111, 501 } 502 503 // Based on Cortex-R8 manual 10.1.2 "PMU management registers" chapter's Table 10-3 504 // "Processor Identifier Registers". The same offsets can be found in the ARMv7AR manual 505 // in chapter C11.10.1 "About the Debug management registers" but describing memory-mapped 506 // debug registers while Table D2-1 "Performance Monitors memory-mapped register views" 507 // specifies 0xCC4-0xD7C offsets to be reserved. It's the reason for having the 508 // `withProcessorIdMMIORegisters` construction parameter. 509 private enum ProcessorIdRegisters 510 { 511 MainID_MIDR = 0xD00, 512 CacheType_CTR = 0xD04, 513 TCMType_TCMTR = 0xD08, 514 TLBType_TLBTR = 0xD0C, 515 MPUType_MPUIR = 0xD10, 516 MultiprocessorAffinity_MPIDR = 0xD14, 517 RevisionID_REVIDR = 0xD18, 518 MainIDAlias = 0xD1C, 519 ProcessorFeature0_ID_PFR0 = 0xD20, 520 ProcessorFeature1_ID_PFR1 = 0xD24, 521 DebugFeature0_ID_DFR0 = 0xD28, 522 AuxiliaryFeature0_ID_AFR0 = 0xD2C, 523 MemoryModelFeature0_ID_MMFR0 = 0xD30, 524 MemoryModelFeature1_ID_MMFR1 = 0xD34, 525 MemoryModelFeature2_ID_MMFR2 = 0xD38, 526 MemoryModelFeature3_ID_MMFR3 = 0xD3C, 527 InstructionSetAttribute0_ID_ISAR0 = 0xD40, 528 InstructionSetAttribute1_ID_ISAR1 = 0xD44, 529 InstructionSetAttribute2_ID_ISAR2 = 0xD48, 530 InstructionSetAttribute3_ID_ISAR3 = 0xD4C, 531 InstructionSetAttribute4_ID_ISAR4 = 0xD50, 532 InstructionSetAttribute5_ID_ISAR5 = 0xD54, 533 } 534 535 // Memory-mapped PMU registers; PMSELR isn't accessible because all counters can be 536 // accessed directly. At 0xFxx there are extra registers not available through CP15. 537 private enum Registers 538 { 539 // Based on ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition's Table D2-1 540 // "Performance Monitors memory-mapped register views" and Arm CoreSight Architecture 541 // Performance Monitoring Unit Architecture's (further referenced as "CoreSight PMU 542 // Specification") Table 3.1 "Memory-mapped register map". 543 // 544 // CoreSight PMU Specification's `PMCCNTR` offset, 0x3C, seems to be invalid. Its ID 545 // is 31, which is even mentioned in the "2.6.3 Fixed-function cycle counter extension" 546 // so it should be 0x7C as in the ARMv7AR manual. That's what will be used here. 547 // 548 // The offsets are for CoreSight PMU without dual-page extension for 32-bit registers. 549 EventCount0 = 0x000, // PMXEVCNTR<0> (PMEVCNTR in CoreSight PMU Specification) 550 CycleCount = 0x07C, // PMCCNTR (PMXEVCNTR<31>) 551 EventTypeSelect0 = 0x400, // PMXEVTYPER<0> (PMEVTYPER in CoreSight PMU Specification) 552 CountEnableSet = 0xC00, // PMCNTENSET 553 CountEnableClear = 0xC20, // PMCNTENCLR 554 InterruptEnableSet = 0xC40, // PMINTENSET 555 InterruptEnableClear = 0xC60, // PMINTENCLR 556 OverflowFlagStatus = 0xC80, // PMOVSR 557 SoftwareIncrement = 0xCA0, // PMSWINC 558 OverflowFlagStatusSet = 0xCC0, // PMOVSSET; not implemented, it's only in VMSA CPUs with Virtualization Extensions 559 Configuration = 0xE00, // PMCFGR, there's no system register equivalent 560 Control = 0xE04, // PMCR 561 UserEnable = 0xE08, // PMUSERENR 562 CommonEventIdentification0 = 0xE20, // PMCEID0 563 CommonEventIdentification1 = 0xE24, // PMCEID1 564 565 // There are no system register equivalents for any of the registers that follow. 566 567 // The CoreSight PMU Specification contains neither `PMCLAIM*` nor `PML*R` registers. 568 // ARMv7AR manual says "PMLAR is UNPREDICTABLE in the external debug interface" with 569 // something similar for `PMLSR` so it seems those should only be accessible with MMIO 570 // accesses. `PMCLAIM*` registers aren't described at all so their implementation is 571 // based on the `DBGCLAIM*` registers. 572 ClaimTagSet = 0xFA0, // PMCLAIMSET 573 ClaimTagClear = 0xFA4, // PMCLAIMCLR 574 LockAccess = 0xFB0, // PMLAR 575 LockStatus = 0xFB4, // PMLSR 576 AuthenticationStatus = 0xFB8, // PMAUTHSTATUS 577 578 // The registers below seem to be required for CoreSight mostly. 579 DeviceConfiguration = 0xFC8, // PMDEVID 580 DeviceType = 0xFCC, // PMDEVTYPE 581 PeripheralIdentification4 = 0xFD0, // PMPID4 582 PeripheralIdentification5 = 0xFD4, // PMPID5 583 PeripheralIdentification6 = 0xFD8, // PMPID6 584 PeripheralIdentification7 = 0xFDC, // PMPID7 585 PeripheralIdentification0 = 0xFE0, // PMPID0 586 PeripheralIdentification1 = 0xFE4, // PMPID1 587 PeripheralIdentification2 = 0xFE8, // PMPID2 588 PeripheralIdentification3 = 0xFEC, // PMPID3 589 ComponentIdentification0 = 0xFF0, // PMCID0 590 ComponentIdentification1 = 0xFF4, // PMCID1 591 ComponentIdentification2 = 0xFF8, // PMCID2 592 ComponentIdentification3 = 0xFFC, // PMCID3 593 } 594 } 595 } 596