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