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 System.Runtime.InteropServices;
11 
12 using Antmicro.Renode.Core;
13 using Antmicro.Renode.Core.Structure;
14 using Antmicro.Renode.Debugging;
15 using Antmicro.Renode.Exceptions;
16 using Antmicro.Renode.Logging;
17 using Antmicro.Renode.Peripherals.Bus;
18 using Antmicro.Renode.Peripherals.CPU;
19 using Antmicro.Renode.Peripherals.IRQControllers;
20 using Antmicro.Renode.Peripherals.Timers;
21 using Antmicro.Renode.Utilities;
22 
23 namespace Antmicro.Renode.Peripherals.Miscellaneous
24 {
25     public class CortexR5SignalsUnit : ArmSignalsUnit
26     {
CortexR5SignalsUnit(IMachine machine)27         public CortexR5SignalsUnit(IMachine machine) : base(machine, UnitType.CortexR5)
28         {
29             // Intentionally left empty.
30         }
31     }
32 
33     public class CortexR8SignalsUnit : ArmSignalsUnit
34     {
CortexR8SignalsUnit(IMachine machine, ArmSnoopControlUnit snoopControlUnit)35         public CortexR8SignalsUnit(IMachine machine, ArmSnoopControlUnit snoopControlUnit)
36             : base(machine, UnitType.CortexR8, snoopControlUnit)
37         {
38             // Intentionally left empty.
39         }
40     }
41 
42     public class ArmSignalsUnit : IPeripheral, ISignalsUnit
43     {
ArmSignalsUnit(IMachine machine, UnitType unitType, ArmSnoopControlUnit snoopControlUnit = null)44         protected ArmSignalsUnit(IMachine machine, UnitType unitType, ArmSnoopControlUnit snoopControlUnit = null)
45         {
46             this.machine = machine;
47             this.unitType = unitType;
48             InitSignals(unitType);
49 
50             // SCU is required for Cortex-R8 PERIPHBASE logic but, for example, Cortex-R5 has neither PERIPHBASE nor is used with SCU.
51             if(unitType == UnitType.CortexR8)
52             {
53                 this.snoopControlUnit = snoopControlUnit
54                     ?? throw new ConstructionException($"{nameof(snoopControlUnit)} is required in {unitType}SignalsUnit");
55 
56                 // PeripheralsBase initialization is postponed to the moment of adding SnoopControlUnit to the platform.
57                 machine.PeripheralsChanged += OnMachinePeripheralsChanged;
58             }
59             else if(snoopControlUnit != null)
60             {
61                 throw new ConstructionException($"{nameof(snoopControlUnit)} can't be used in {unitType}SignalsUnit");
62             }
63         }
64 
FillConfigurationStateStruct(IntPtr allocatedStructPointer, Arm cpu)65         public void FillConfigurationStateStruct(IntPtr allocatedStructPointer, Arm cpu)
66         {
67             var registeredCPU = GetRegisteredCPU(cpu);
68             registeredCPU.FillConfigurationStateStruct(allocatedStructPointer);
69         }
70 
GetAddress(string name)71         public ulong GetAddress(string name)
72         {
73             return GetAddress(signals.Parse(name));
74         }
75 
GetAddress(ArmSignals armSignal)76         public ulong GetAddress(ArmSignals armSignal)
77         {
78             var signal = signals[armSignal];
79             AssertSignalNotCPUIndexed(signal, inSetMethod: false);
80 
81             return signal.GetAddress(AddressWidth);
82         }
83 
GetSignal(string name)84         public ulong GetSignal(string name)
85         {
86             return GetSignal(signals.Parse(name));
87         }
88 
GetSignal(ArmSignals armSignal)89         public ulong GetSignal(ArmSignals armSignal)
90         {
91             var signal = signals[armSignal];
92             AssertSignalNotCPUIndexed(signal, inSetMethod: false);
93 
94             return signal.Value;
95         }
96 
IsSignalEnabled(string name)97         public bool IsSignalEnabled(string name)
98         {
99             return IsSignalEnabled(signals.Parse(name));
100         }
101 
IsSignalEnabled(ArmSignals armSignal)102         public bool IsSignalEnabled(ArmSignals armSignal)
103         {
104             var signal = signals[armSignal];
105             AssertSignalNotCPUIndexed(signal, inSetMethod: false);
106 
107             return signal.IsEnabled();
108         }
109 
IsSignalEnabledForCPU(string name, ICPU cpu)110         public bool IsSignalEnabledForCPU(string name, ICPU cpu)
111         {
112             return IsSignalEnabledForCPU(signals.Parse(name), cpu);
113         }
114 
IsSignalEnabledForCPU(ArmSignals armSignal, ICPU cpu)115         public bool IsSignalEnabledForCPU(ArmSignals armSignal, ICPU cpu)
116         {
117             var signal = signals[armSignal];
118             AssertSignalCPUIndexed(signal, inSetMethod: false);
119 
120             var cpuIndex = GetRegisteredCPU(cpu).Index;
121             return signals[armSignal].IsEnabled(cpuIndex);
122         }
123 
124         // Called in Arm constructors if ArmSignalsUnit passed.
RegisterCPU(Arm cpu)125         public void RegisterCPU(Arm cpu)
126         {
127             lock(registeredCPUs)
128             {
129                 AssertCPUModelIsSupported(cpu.Model);
130 
131                 // Bit offset for this CPU in CPU-indexed signals.
132                 var cpuIndex = registeredCPUs.Count;
133                 registeredCPUs[cpu] = new RegisteredCPU(machine, cpu, this, cpuIndex);
134                 signals.SetCPUIndexedSignalsWidth((uint)registeredCPUs.Count);
135             }
136 
137             cpu.StateChanged += (_, oldState, __) => {
138                 if(oldState == EmulationCPUState.InReset)
139                 {
140                     if(unitType == UnitType.CortexR8)
141                     {
142                         lock(registeredCPUs)
143                         {
144                             if(firstSCURegistration is NullRegistrationPoint && !scuRegisteredAtBus)
145                             {
146                                 RegisterSCU();
147                                 scuRegisteredAtBus = true;
148                             }
149                         }
150                     }
151                     registeredCPUs[cpu].OnCPUOutOfReset();
152                 };
153             };
154         }
155 
Reset()156         public void Reset()
157         {
158             // Intentionally left blank. Signal values should be preserved across machine resets.
159         }
160 
ResetSignals()161         public void ResetSignals()
162         {
163             signals.Reset();
164         }
165 
SetSignal(string name, ulong value)166         public void SetSignal(string name, ulong value)
167         {
168             SetSignal(signals.Parse(name), value);
169         }
170 
SetSignal(ArmSignals armSignal, ulong value)171         public void SetSignal(ArmSignals armSignal, ulong value)
172         {
173             var signal = signals[armSignal];
174             AssertSignalNotCPUIndexed(signal, inSetMethod: true);
175 
176             signal.Value = value;
177         }
178 
179         // Convenience methods for signals which are meant to hold top bits of address.
180         // There's no need to check if such a signal was chosen, a RecoverableException
181         // is thrown if the signal can't hold the given address.
SetSignalFromAddress(string name, ulong address)182         public void SetSignalFromAddress(string name, ulong address)
183         {
184             SetSignalFromAddress(signals.Parse(name), address);
185         }
186 
SetSignalFromAddress(ArmSignals armSignal, ulong address)187         public void SetSignalFromAddress(ArmSignals armSignal, ulong address)
188         {
189             var signal = signals[armSignal];
190             AssertSignalNotCPUIndexed(signal, inSetMethod: true);
191 
192             signal.SetFromAddress(AddressWidth, address);
193         }
194 
SetSignalState(string name, bool state, uint index)195         public void SetSignalState(string name, bool state, uint index)
196         {
197             SetSignalState(signals.Parse(name), state, index);
198         }
199 
SetSignalState(ArmSignals armSignal, bool state, uint index)200         public void SetSignalState(ArmSignals armSignal, bool state, uint index)
201         {
202             var signal = signals[armSignal];
203             AssertSignalNotCPUIndexed(signal, inSetMethod: true);
204 
205             signal.SetState(checked((byte)index), state);
206         }
207 
SetSignalStateForCPU(string name, bool state, ICPU cpu)208         public void SetSignalStateForCPU(string name, bool state, ICPU cpu)
209         {
210             SetSignalStateForCPU(signals.Parse(name), state, cpu);
211         }
212 
SetSignalStateForCPU(ArmSignals armSignal, bool state, ICPU cpu)213         public void SetSignalStateForCPU(ArmSignals armSignal, bool state, ICPU cpu)
214         {
215             var signal = signals[armSignal];
216             AssertSignalCPUIndexed(signal, inSetMethod: true);
217 
218             var cpuIndex = GetRegisteredCPU(cpu).Index;
219             signal.SetState(cpuIndex, state);
220         }
221 
222         public uint AddressWidth { get; } = 32;
223         public IEnumerable<ICPU> RegisteredCPUs => registeredCPUs.Keys;
224 
AssertCPUModelIsSupported(string cpuModel)225         private void AssertCPUModelIsSupported(string cpuModel)
226         {
227             var supportedModels = ModelsToUnitTypes.Where(kvPair => kvPair.Value == unitType).Select(kvPair => kvPair.Key);
228 
229             if(!supportedModels.Contains(cpuModel))
230             {
231                 var message = $"Tried to register unsupported CPU model to {unitType}SignalsUnit: {cpuModel}; supported CPUs are: {string.Join(", ", supportedModels)}";
232                 throw new RecoverableException(message);
233             }
234         }
235 
AssertSignalCPUIndexed(Signal<ArmSignals> signal, bool inSetMethod)236         private void AssertSignalCPUIndexed(Signal<ArmSignals> signal, bool inSetMethod)
237         {
238             if(!signals.IsSignalCPUIndexed(signal))
239             {
240                 var alternativeMethodNames = inSetMethod
241                     ? new string[] { nameof(SetSignalFromAddress), nameof(SetSignal), nameof(SetSignalState) }
242                     : new string[] { nameof(GetAddress), nameof(GetSignal), nameof(IsSignalEnabled) };
243                 throw new RecoverableException($"Signal is not CPU-indexed. Use '{string.Join("' or '", alternativeMethodNames)}' to access it.");
244             }
245         }
246 
AssertSignalNotCPUIndexed(Signal<ArmSignals> signal, bool inSetMethod)247         private void AssertSignalNotCPUIndexed(Signal<ArmSignals> signal, bool inSetMethod)
248         {
249             if(signals.IsSignalCPUIndexed(signal))
250             {
251                 var alternativeMethodName = inSetMethod ? nameof(SetSignalStateForCPU) : nameof(IsSignalEnabledForCPU);
252                 throw new RecoverableException($"Signal is CPU-indexed. Use '{alternativeMethodName}' to access it.");
253             }
254         }
255 
GetRegisteredCPU(ICPU cpu)256         private RegisteredCPU GetRegisteredCPU(ICPU cpu)
257         {
258             if(!registeredCPUs.TryGetValue(cpu, out var registeredCPU))
259             {
260                 // The exception isn't always expected to be caught, e.g., when called by CPU through 'FillConfigurationStateStruct'.
261                 throw new RecoverableException($"CPU '{cpu.GetName()}' isn't registered to this signals unit '{this.GetName()}'.");
262             }
263             return registeredCPU;
264         }
265 
InitSignals(UnitType type)266         private void InitSignals(UnitType type)
267         {
268             signals.InitSignal(this, "DBGROMADDR", ArmSignals.DebugROMAddress, width: 20);
269             signals.InitSignal(this, "DBGROMADDRV", ArmSignals.DebugROMAddressValid, width: 1);
270             signals.InitSignal(this, "DBGSELFADDR", ArmSignals.DebugSelfAddress, width: 15);
271             signals.InitSignal(this, "DBGSELFADDRV", ArmSignals.DebugSelfAddressValid, width: 1);
272 
273             // CPU-indexed signals have width equal to CPUs count since there's a single bit per CPU.
274             signals.InitSignal(this, "INITRAM", ArmSignals.InitializeInstructionTCM, cpuIndexedSignal: true);
275             signals.InitSignal(this, "VINITHI", ArmSignals.HighExceptionVectors, cpuIndexedSignal: true);
276 
277             switch(type)
278             {
279             case UnitType.CortexR5:
280                 // Cortex-R5 AHB/AXI peripheral interface signals, Virtual AXI has only base and size.
281                 signals.InitSignal(this, "INITPPH", ArmSignals.AHBInitEnabled, cpuIndexedSignal: true);
282                 signals.InitSignal(this, "INITPPX", ArmSignals.AXIInitEnabled, cpuIndexedSignal: true);
283 
284                 // Currently both CPUs share the same base and size values (R5 can only be dual-core).
285                 signals.InitSignal(this, "PPHBASE", ArmSignals.AHBBaseAddress, width: 20);
286                 signals.InitSignal(this, "PPXBASE", ArmSignals.AXIBaseAddress, width: 20);
287                 signals.InitSignal(this, "PPVBASE", ArmSignals.VirtualAXIBaseAddress, width: 20);
288 
289                 signals.InitSignal(this, "PPHSIZE", ArmSignals.AHBSize, width: 5);
290                 signals.InitSignal(this, "PPXSIZE", ArmSignals.AXISize, width: 5);
291                 signals.InitSignal(this, "PPVSIZE", ArmSignals.VirtualAXISize, width: 5);
292                 break;
293             case UnitType.CortexR8:
294                 signals.InitSignal(this, "MFILTEREN", ArmSignals.MasterFilterEnable, width: 1);
295                 signals.InitSignal(this, "MFILTEREND", ArmSignals.MasterFilterEnd, width: 12);
296                 signals.InitSignal(this, "MFILTERSTART", ArmSignals.MasterFilterStart, width: 12);
297                 signals.InitSignal(this, "PERIPHBASE", ArmSignals.PeripheralsBase, width: PeripheralsBaseBits);
298                 signals.InitSignal(this, "PFILTEREND", ArmSignals.PeripheralFilterEnd, width: 12);
299                 signals.InitSignal(this, "PFILTERSTART", ArmSignals.PeripheralFilterStart, width: 12);
300                 break;
301             default:
302                 throw new RecoverableException($"Invalid {nameof(type)} value: {type}");
303             }
304         }
305 
OnMachinePeripheralsChanged(IMachine machine, PeripheralsChangedEventArgs args)306         private void OnMachinePeripheralsChanged(IMachine machine, PeripheralsChangedEventArgs args)
307         {
308             if(args.Peripheral == snoopControlUnit && args is PeripheralsAddedEventArgs addedArgs)
309             {
310                 var peripheralsBase = signals[ArmSignals.PeripheralsBase];
311                 lock(peripheralsBase)
312                 {
313                     OnSnoopControlUnitAdded(peripheralsBase, addedArgs.RegistrationPoint);
314                 }
315             }
316         }
317 
OnSnoopControlUnitAdded(Signal<ArmSignals> peripheralsBase, IRegistrationPoint registrationPoint)318         private void OnSnoopControlUnitAdded(Signal<ArmSignals> peripheralsBase, IRegistrationPoint registrationPoint)
319         {
320             this.DebugLog("Handling SCU's registration: {0}", registrationPoint);
321 
322             if(registrationPoint is IBusRegistration busRegistration)
323             {
324                 OnSnoopControlUnitAdded(peripheralsBase, busRegistration);
325             }
326             else if(registrationPoint is NullRegistrationPoint)
327             {
328                 // This method can be called multiple times with IRegistrationPoint but at most once with NullRegistrationPoint.
329                 DebugHelper.Assert(firstSCURegistration == null);
330             }
331             else
332             {
333                 throw new RecoverableException($"{this.GetName()}: Added {nameof(ArmSnoopControlUnit)} with unsupported registration point!");
334             }
335 
336             if(firstSCURegistration == null)
337             {
338                 firstSCURegistration = registrationPoint;
339             }
340         }
341 
OnSnoopControlUnitAdded(Signal<ArmSignals> peripheralsBase, IBusRegistration busRegistration)342         private void OnSnoopControlUnitAdded(Signal<ArmSignals> peripheralsBase, IBusRegistration busRegistration)
343         {
344             var context = busRegistration.Initiator as ICPU;
345             if(context != null && !registeredCPUs.ContainsKey(context))
346             {
347                 this.DebugLog("Ignoring {0} registration for CPU unregistered in {1}: {2}",
348                     nameof(ArmSnoopControlUnit), nameof(ArmSignalsUnit), context
349                 );
350                 return;
351             }
352 
353             if(firstSCURegistration == null)
354             {
355                 // SnoopControlUnit's address indicates PeripheralsBase address cause its offset is 0x0.
356                 peripheralsBase.SetFromAddress(AddressWidth, busRegistration.StartingPoint);
357                 peripheralsBase.ResetValue = peripheralsBase.Value;
358             }
359             // Let's make sure the address has been the same in all bus registrations.
360             else if(firstSCURegistration is IBusRegistration firstSCUBusRegistration
361                     && busRegistration.StartingPoint != firstSCUBusRegistration.StartingPoint)
362             {
363                 throw new RecoverableException("All SCU registrations must use the same address");
364             }
365 
366             // Casting must be successful because `context` is in `registeredCPUs.Keys`.
367             var cpus = context == null ? registeredCPUs.Keys.ToArray() : new[] { (Arm)context };
368             foreach(var cpu in cpus)
369             {
370                 if(context == null && registeredCPUs[cpu].PeripheralsBaseAtLastReset.HasValue)
371                 {
372                     // It must've been already set using CPU-specific registration point.
373                     continue;
374                 }
375                 registeredCPUs[cpu].PeripheralsBaseAtLastReset = peripheralsBase.Value;
376             }
377         }
378 
RegisterSCU()379         private void RegisterSCU()
380         {
381             // The name is lost when the peripheral gets unregistered. It's unregistered because there
382             // are no SCU registrations in `peripherals` command with `NullRegistrationPoint` left.
383             var scuName = machine.GetLocalName(snoopControlUnit);
384             machine.SystemBus.Unregister(snoopControlUnit);
385 
386             foreach(var registeredCPU in registeredCPUs.Values)
387             {
388                 registeredCPU.RegisterSCU(snoopControlUnit);
389             }
390             machine.SetLocalName(snoopControlUnit, scuName);
391         }
392 
393         private IRegistrationPoint firstSCURegistration;
394         private bool scuRegisteredAtBus;
395 
396         private readonly IMachine machine;
397         private readonly Dictionary<ICPU, RegisteredCPU> registeredCPUs = new Dictionary<ICPU, RegisteredCPU>();
398         private readonly SignalsDictionary<ArmSignals> signals = new SignalsDictionary<ArmSignals>();
399         private readonly ArmSnoopControlUnit snoopControlUnit;
400         private readonly UnitType unitType;
401 
402         private const int PeripheralsBaseBits = 19;
403 
404         private static readonly Dictionary<string, UnitType> ModelsToUnitTypes = new Dictionary<string, UnitType>
405         {
406             {"cortex-r5", UnitType.CortexR5},
407             {"cortex-r5f", UnitType.CortexR5},
408             {"cortex-r8", UnitType.CortexR8},
409         };
410 
411         public enum UnitType
412         {
413             CortexR5,
414             CortexR8,
415         }
416 
417         private class RegisteredCPU
418         {
RegisteredCPU(IMachine machine, Arm cpu, ArmSignalsUnit signalsUnit, int index)419             public RegisteredCPU(IMachine machine, Arm cpu, ArmSignalsUnit signalsUnit, int index)
420             {
421                 this.cpu = cpu ?? throw new ArgumentNullException(nameof(cpu));
422                 this.machine = machine;
423                 this.signalsUnit = signalsUnit;
424 
425                 Index = (byte)index;
426             }
427 
FillConfigurationStateStruct(IntPtr allocatedStructPointer)428             public void FillConfigurationStateStruct(IntPtr allocatedStructPointer)
429             {
430                 var state = new ConfigurationSignalsState
431                 {
432                     IncludedSignalsMask = IncludedConfigurationSignalsMask.Create(
433                         signalsUnit.unitType,
434                         signalsUnit.IsSignalEnabled(ArmSignals.DebugROMAddressValid),
435                         signalsUnit.IsSignalEnabled(ArmSignals.DebugSelfAddressValid)
436                     ),
437 
438                     DebugROMAddress = signalsUnit.IsSignalEnabled(ArmSignals.DebugROMAddressValid)
439                         ? (uint)signalsUnit.GetSignal(ArmSignals.DebugROMAddress) : 0u,
440                     DebugSelfAddress = signalsUnit.IsSignalEnabled(ArmSignals.DebugSelfAddressValid)
441                         ? (uint)signalsUnit.GetSignal(ArmSignals.DebugSelfAddress) : 0u,
442 
443                     HighExceptionVectors = signalsUnit.IsSignalEnabledForCPU(ArmSignals.HighExceptionVectors, cpu) ? 1u : 0u,
444                     InitializeInstructionTCM = signalsUnit.IsSignalEnabledForCPU(ArmSignals.InitializeInstructionTCM, cpu) ? 1u : 0u,
445                 };
446 
447                 switch(signalsUnit.unitType)
448                 {
449                 case UnitType.CortexR5:
450                     state.AHBRegionRegister = GetBusRegionRegister(ArmSignals.AHBBaseAddress, ArmSignals.AHBSize, ArmSignals.AHBInitEnabled);
451                     state.AXIRegionRegister = GetBusRegionRegister(ArmSignals.AXIBaseAddress, ArmSignals.AXISize, ArmSignals.AXIInitEnabled);
452                     state.VirtualAXIRegionRegister = GetBusRegionRegister(ArmSignals.VirtualAXIBaseAddress, ArmSignals.VirtualAXISize, initSignal: null);
453                     break;
454                 case UnitType.CortexR8:
455                     state.PeripheralsBase = (uint)signalsUnit.GetSignal(ArmSignals.PeripheralsBase);
456                     break;
457                 default:
458                     throw new RecoverableException($"Invalid {nameof(signalsUnit.unitType)} value: {signalsUnit.unitType}");
459                 }
460                 Marshal.StructureToPtr(state, allocatedStructPointer, fDeleteOld: true);
461             }
462 
OnCPUOutOfReset()463             public void OnCPUOutOfReset()
464             {
465                 if(signalsUnit.IsSignalEnabledForCPU(ArmSignals.InitializeInstructionTCM, cpu)
466                     && signalsUnit.IsSignalEnabledForCPU(ArmSignals.HighExceptionVectors, cpu))
467                 {
468                     cpu.PC = 0xFFFF0000;
469                 }
470 
471                 if(signalsUnit.unitType == UnitType.CortexR8)
472                 {
473                     var peripheralsBase = signalsUnit.GetSignal(ArmSignals.PeripheralsBase);
474                     if(PeripheralsBaseAtLastReset != peripheralsBase)
475                     {
476                         PeripheralsBaseChanged();
477                         PeripheralsBaseAtLastReset = peripheralsBase;
478                     }
479                 }
480             }
481 
PeripheralsBaseChanged()482             public void PeripheralsBaseChanged()
483             {
484                 var peripheralsBaseAddress = signalsUnit.GetAddress(ArmSignals.PeripheralsBase);
485 
486                 var pFilterStart = signalsUnit.GetAddress(ArmSignals.PeripheralFilterStart);
487                 var pFilterEnd = signalsUnit.GetAddress(ArmSignals.PeripheralFilterEnd);
488 
489                 // The signals are just uninitialized if pFilterEnd is 0 so let's not log warnings then.
490                 if(pFilterEnd != 0 && (peripheralsBaseAddress < pFilterStart || peripheralsBaseAddress > pFilterEnd))
491                 {
492                     signalsUnit.Log(LogLevel.Warning, "{0} address 0x{1:X} should be between {2} address (0x{3:X}) and {4} address (0x{5:X})",
493                         Enum.GetName(typeof(ArmSignals), ArmSignals.PeripheralsBase), peripheralsBaseAddress,
494                         Enum.GetName(typeof(ArmSignals), ArmSignals.PeripheralFilterStart), pFilterStart,
495                         Enum.GetName(typeof(ArmSignals), ArmSignals.PeripheralFilterEnd), pFilterEnd);
496                 }
497                 MovePeripherals(peripheralsBaseAddress);
498             }
499 
RegisterSCU(ArmSnoopControlUnit scu)500             public void RegisterSCU(ArmSnoopControlUnit scu)
501             {
502                 var address = signalsUnit.GetAddress(ArmSignals.PeripheralsBase) + (ulong)PeriphbaseOffsets.SnoopControlUnit;
503                 var registrationPoint = new BusRangeRegistration(address.By(checked((ulong)scu.Size)), cpu: cpu);
504                 machine.SystemBus.Register(scu, registrationPoint);
505             }
506 
507             public byte Index { get; }
508 
509             public ulong? PeripheralsBaseAtLastReset;
510 
GetBusRegionRegister(ArmSignals baseSignal, ArmSignals sizeSignal, ArmSignals? initSignal)511             private uint GetBusRegionRegister(ArmSignals baseSignal, ArmSignals sizeSignal, ArmSignals? initSignal)
512             {
513                 var value = 0u;
514                 BitHelper.SetMaskedValue(ref value, (uint)signalsUnit.GetSignal(baseSignal), 12, 20);
515                 BitHelper.SetMaskedValue(ref value, (uint)signalsUnit.GetSignal(sizeSignal), 2, 5);
516                 if(initSignal != null)
517                 {
518                     BitHelper.SetBit(ref value, 0, signalsUnit.IsSignalEnabledForCPU(initSignal.Value, cpu));
519                 }
520                 return value;
521             }
522 
MovePeripherals(ulong peripheralsBaseAddress)523             private void MovePeripherals(ulong peripheralsBaseAddress)
524             {
525                 signalsUnit.DebugLog("Moving GIC, SCU and timers for CPU {0} relatively to new PERIPHBASE value: 0x{1:X}", cpu, peripheralsBaseAddress);
526 
527                 Func<PeriphbaseOffsets, ulong> getAddress = offset => peripheralsBaseAddress + (ulong)offset;
528                 MoveOrRegisterPeripheralWithinContext<ARM_GenericInterruptController>(peripheralsBaseAddress + (ulong)PeriphbaseOffsets.GIC_CPUInterface, "cpuInterface");
529                 MoveOrRegisterPeripheralWithinContext<ARM_GenericInterruptController>(peripheralsBaseAddress + (ulong)PeriphbaseOffsets.GIC_Distributor, "distributor");
530                 MoveOrRegisterPeripheralWithinContext<ARM_GlobalTimer>(peripheralsBaseAddress + (ulong)PeriphbaseOffsets.GlobalTimer);
531                 MoveOrRegisterPeripheralWithinContext<ArmSnoopControlUnit>(peripheralsBaseAddress + (ulong)PeriphbaseOffsets.SnoopControlUnit);
532                 MoveOrRegisterPeripheralWithinContext<ARM_PrivateTimer>(peripheralsBaseAddress + (ulong)PeriphbaseOffsets.PrivateTimersAndWatchdogs);
533             }
534 
535             /// <remarks>Either registers or moves registration for peripheral of type <c>T</c> and the given CPU. Global registrations are left untouched.</remarks>
536             private void MoveOrRegisterPeripheralWithinContext<T>(ulong newAddress, string region = null) where T : IBusPeripheral
537             {
538                 if(!TryGetSingleBusRegistered<T>(region, out var busRegistered))
539                 {
540                     // Peripheral not found or there are multiple peripherals of the specified type in the given context.
541                     var registrationName = string.IsNullOrWhiteSpace(region) ? "" : $"region {region} of" + typeof(T).Name;
542                     signalsUnit.DebugLog("No registration found for {0}; won't be moved to 0x{1:X}", registrationName, newAddress);
543                     return;
544                 }
545                 var peripheral = busRegistered.Peripheral;
546                 var registrationPoint = busRegistered.RegistrationPoint;
547                 var size = registrationPoint.Range.Size;
548 
549                 var newRegistration = registrationPoint is BusMultiRegistration
550                     ? new BusMultiRegistration(newAddress, size, region, cpu)
551                     : new BusRangeRegistration(newAddress, size, cpu: cpu);
552 
553                 if(registrationPoint.Initiator == cpu)
554                 {
555                     if(newRegistration is BusMultiRegistration newMultiRP)
556                     {
557                         machine.SystemBus.MoveBusMultiRegistrationWithinContext(peripheral, newMultiRP, cpu);
558                     }
559                     else
560                     {
561                         machine.SystemBus.MoveRegistrationWithinContext(peripheral, newRegistration, cpu);
562                     }
563                 }
564                 else
565                 {
566                     machine.SystemBus.Register(peripheral, newRegistration);
567                 }
568             }
569 
570             /// <remarks>It will be a BusRegistered with CPU-local registration point, if exists. Global registration points are checked only if there are no CPU-local ones.</remarks>
571             private bool TryGetSingleBusRegistered<T>(string region, out IBusRegistered<IBusPeripheral> busRegistered) where T : IBusPeripheral
572             {
573                 busRegistered = null;
574                 var busRegisteredEnumerable = machine.SystemBus.GetRegisteredPeripherals(cpu).Where(_busRegistered => _busRegistered.Peripheral is T);
575 
576                 if(busRegisteredEnumerable.Any() && !string.IsNullOrEmpty(region))
577                 {
578                     busRegisteredEnumerable = busRegisteredEnumerable.Where(
579                         _busRegistered => _busRegistered.RegistrationPoint is BusMultiRegistration multiRegistration
580                         && multiRegistration.ConnectionRegionName == region
581                     );
582                 }
583 
584                 // Choose cpu-local registrations if there are still multiple matching ones.
585                 if(busRegisteredEnumerable.Count() > 1)
586                 {
587                     busRegisteredEnumerable = busRegisteredEnumerable.Where(_busRegistered => _busRegistered.RegistrationPoint.Initiator == cpu);
588                 }
589 
590                 var count = (uint)busRegisteredEnumerable.Count();
591                 if(count > 1)
592                 {
593                     var logLine = "Multiple matching {0}"
594                         .AppendIf(!string.IsNullOrEmpty(region), $" (region: {region})")
595                         .Append(" registration points")
596                         .AppendIf(cpu != null, $" for {cpu}")
597                         .ToString();
598                     signalsUnit.Log(LogLevel.Warning, logLine, typeof(T).Name);
599                 }
600                 busRegistered = busRegisteredEnumerable.SingleOrDefault();
601                 return busRegistered != default(IBusRegistered<IBusPeripheral>);
602             }
603 
604             private readonly Arm cpu;
605             private readonly IMachine machine;
606             private readonly ArmSignalsUnit signalsUnit;
607 
608             private static class IncludedConfigurationSignalsMask
609             {
Create(UnitType unitType, bool debugRomAddressValid, bool debugSelfAddressValid)610                 public static ulong Create(UnitType unitType, bool debugRomAddressValid, bool debugSelfAddressValid)
611                 {
612                     var mask = (debugRomAddressValid ? 1u : 0u) << (int)SignalsEnumSharedWithTlib.DebugROMAddress
613                         | (debugSelfAddressValid ? 1u : 0u) << (int)SignalsEnumSharedWithTlib.DebugSelfAddress
614                         | 1u << (int)SignalsEnumSharedWithTlib.HighExceptionVectors
615                         | 1u << (int)SignalsEnumSharedWithTlib.InitializeInstructionTCM
616                     ;
617 
618                     switch(unitType)
619                     {
620                     case UnitType.CortexR5:
621                         mask |= 1u << (int)SignalsEnumSharedWithTlib.AHBRegionRegister;
622                         mask |= 1u << (int)SignalsEnumSharedWithTlib.AXIRegionRegister;
623                         mask |= 1u << (int)SignalsEnumSharedWithTlib.VirtualAXIRegionRegister;
624                         break;
625                     case UnitType.CortexR8:
626                         mask |= 1u << (int)SignalsEnumSharedWithTlib.PeripheralsBase;
627                         break;
628                     default:
629                         throw new RecoverableException($"Invalid {nameof(unitType)} value: {unitType}");
630                     }
631                     return mask;
632                 }
633 
634                 // Copy of ConfigurationSignals enum in tlib's arm/configuration_signals.h
635                 private enum SignalsEnumSharedWithTlib
636                 {
637                     DebugROMAddress,
638                     DebugSelfAddress,
639                     HighExceptionVectors,
640                     InitializeInstructionTCM,
641                     PeripheralsBase,
642                     AHBRegionRegister,
643                     AXIRegionRegister,
644                     VirtualAXIRegionRegister,
645                 }
646             }
647 
648             // Keep in line with ConfigurationSignalsState struct in tlib's arm/configuration_signals.h
649             [StructLayout(LayoutKind.Sequential)]
650             private struct ConfigurationSignalsState
651             {
652                 // Each bit says whether the signal should be analyzed.
653                 // Bit positions are based on the ConfigurationSignals enum.
654                 // Bools can be used here but not in tlib's enum.
655                 public ulong IncludedSignalsMask;
656 
657                 public uint DebugROMAddress;
658                 public uint DebugSelfAddress;
659                 public uint HighExceptionVectors;
660                 public uint InitializeInstructionTCM;
661                 public uint PeripheralsBase;
662 
663                 public uint AHBRegionRegister;
664                 public uint AXIRegionRegister;
665                 public uint VirtualAXIRegionRegister;
666             }
667 
668             private enum PeriphbaseOffsets : ulong
669             {
670                 SnoopControlUnit = 0x0,
671                 GIC_CPUInterface = 0x100,
672                 GlobalTimer = 0x200,
673                 PrivateTimersAndWatchdogs = 0x600,
674                 GIC_Distributor = 0x1000,
675             }
676         }
677 
678         private class SignalsDictionary<TEnum>
679             where TEnum: struct
680         {
SignalsDictionary()681             public SignalsDictionary()
682             {
683                 if(!typeof(TEnum).IsEnum)  // System.Enum as a constraint isn't available in C# 7.2.
684                 {
685                     throw new ConstructionException("T must be enum");
686                 }
687             }
688 
InitSignal(IPeripheral parent, string name, TEnum signal, uint width = 0, ulong resetValue = 0x0, Func<ulong, ulong> getter = null, Action<ulong, ulong> setter = null, bool callSetterAtInitAndReset = false, bool cpuIndexedSignal = false)689             public void InitSignal(IPeripheral parent, string name, TEnum signal, uint width = 0,
690                 ulong resetValue = 0x0, Func<ulong, ulong> getter = null, Action<ulong, ulong> setter = null,
691                 bool callSetterAtInitAndReset = false, bool cpuIndexedSignal = false)
692             {
693                 // Non-negative width is asserted when the signal gets created.
694                 if(width == 0 && !cpuIndexedSignal)
695                 {
696                     throw new ConstructionException($"Invalid init for '{name}' signal. Non-CPU-indexed signals must have positive width.");
697                 }
698 
699                 signalNames.Add(name, signal);
700                 Signal<TEnum> signalObject;
701                 if(getter != null)
702                 {
703                     signalObject = SignalWithImmediateEffect<TEnum>.CreateOutput(parent, signal, width, getter);
704                 }
705                 else if(setter != null)
706                 {
707                     signalObject = SignalWithImmediateEffect<TEnum>.CreateInput(parent, signal, width, setter, callSetterAtInitAndReset, resetValue);
708                 }
709                 else
710                 {
711                     signalObject = new Signal<TEnum>(parent, signal, width, resetValue);
712                 }
713                 dictionary.Add(signal, signalObject);
714 
715                 if(cpuIndexedSignal)
716                 {
717                     cpuIndexedSignals.Add(signalObject);
718                 }
719             }
720 
IsSignalCPUIndexed(Signal<TEnum> signal)721             public bool IsSignalCPUIndexed(Signal<TEnum> signal)
722             {
723                 return cpuIndexedSignals.Contains(signal);
724             }
725 
Parse(string name)726             public TEnum Parse(string name)
727             {
728                 if(!signalNames.TryGetValue(name, out var signal) && !Enum.TryParse(name, ignoreCase: true, out signal))
729                 {
730                     var allNames = signalNames.Keys.Select(_name => $"{_name} ({signalNames[_name]})");
731                     throw new RecoverableException(
732                         $"No such signal: '{name}'\n" +
733                         $"Available signals are:\n * {string.Join("\n * ", allNames)}"
734                         );
735                 }
736                 return signal;
737             }
738 
Reset()739             public void Reset()
740             {
741                 foreach(var signal in dictionary.Values)
742                 {
743                     signal.Reset();
744                 }
745             }
746 
SetCPUIndexedSignalsWidth(uint width)747             public void SetCPUIndexedSignalsWidth(uint width)
748             {
749                 foreach(var signal in cpuIndexedSignals)
750                 {
751                     signal.Width = width;
752                 }
753             }
754 
755             public Signal<TEnum> this[TEnum key] => dictionary[key];
756 
757             private readonly List<Signal<TEnum>> cpuIndexedSignals = new List<Signal<TEnum>>();
758             private readonly Dictionary<TEnum, Signal<TEnum>> dictionary = new Dictionary<TEnum, Signal<TEnum>>();
759             private readonly Dictionary<string, TEnum> signalNames = new Dictionary<string, TEnum>(StringComparer.InvariantCultureIgnoreCase);
760         }
761 
762         private class Signal<TEnum>
763             where TEnum: struct
764         {
Signal(IPeripheral parent, TEnum signal, uint width, ulong resetValue = 0x0)765             public Signal(IPeripheral parent, TEnum signal, uint width, ulong resetValue = 0x0)
766             {
767                 if(!typeof(TEnum).IsEnum)  // System.Enum as a constraint isn't available in C# 7.2.
768                 {
769                     throw new ConstructionException("T must be enum");
770                 }
771                 var name = Enum.GetName(typeof(TEnum), signal) ?? throw new ConstructionException("Invalid signal");
772                 this.parent = parent;
773 
774                 if(width < 0 || width > 64)
775                 {
776                     throw new ConstructionException($"Invalid signal width for {name}: {width}");
777                 }
778                 Width = width;
779 
780                 Name = name;
781                 ResetValue = resetValue;
782 
783                 Reset();
784             }
785 
786             /// <returns>Top <c>Value</c> bits shifted as top bits up to <c>addressWidth</c>.</returns>
GetAddress(uint addressWidth)787             public ulong GetAddress(uint addressWidth)
788             {
789                 AssertAddressWidth(addressWidth, Width);
790 
791                 var offset = addressWidth - Width;
792                 return Value << (int)offset;
793             }
794 
IsEnabled()795             public bool IsEnabled()
796             {
797                 if(Width != 1)
798                 {
799                     throw new RecoverableException($"Signal.IsEnabled: {Name} signal has more than 1 bit, specify which to return");
800                 }
801                 return IsEnabled(index: 0);
802             }
803 
IsEnabled(byte index)804             public bool IsEnabled(byte index)
805             {
806                 if(index >= Width)
807                 {
808                     throw new RecoverableException($"Signal.IsEnabled: {Name} signal has {Width} bits, requested: {Name}[{index}]");
809                 }
810                 return BitHelper.IsBitSet(Value, index);
811             }
812 
Reset()813             public virtual void Reset()
814             {
815                 value = ResetValue;
816             }
817 
818             /// <summary>Top <c>Value</c> bits will be set from the address bits up to <c>Width</c>.</summary>
SetFromAddress(uint addressWidth, ulong address)819             public void SetFromAddress(uint addressWidth, ulong address)
820             {
821                 AssertAddressWidth(addressWidth, Width);
822 
823                 var offset = addressWidth - Width;
824                 if((address & BitHelper.CalculateMask((int)Width, (int)offset)) != address)
825                 {
826                     ThrowException($"{Width}-bit signal in a {addressWidth}-bit unit shouldn't be set from 0x{address:X} address");
827                 }
828                 Value = address >> (int)offset;
829             }
830 
SetState(byte index, bool state)831             public void SetState(byte index, bool state)
832             {
833                 var newValue = Value;
834                 BitHelper.SetBit(ref newValue, index, state);
835                 Value = newValue;
836             }
837 
838             public string Name { get; }
839             public ulong ResetValue { get => resetValue; set => SetValue(ref resetValue, value); }
840             public virtual ulong Value { get => value; set => SetValue(ref this.value, value); }
841             public uint Width
842             {
843                 get => width;
844                 set
845                 {
846                     width = value;
847                     // Re-set values after changing width; old values might be invalid now.
848                     SetValue(ref resetValue, resetValue);
849                     SetValue(ref this.value, this.value);
850                 }
851             }
852 
AssertAddressWidth(uint addressWidth, uint valueWidth)853             private void AssertAddressWidth(uint addressWidth, uint valueWidth)
854             {
855                 if(addressWidth < valueWidth)
856                 {
857                     ThrowException($"Can't convert {valueWidth}-bit signal from or to {addressWidth}-bit address");
858                 }
859             }
860 
SetValue(ref ulong destination, ulong value)861             private void SetValue(ref ulong destination, ulong value)
862             {
863                 if(BitHelper.GetMaskedValue(value, 0, (int)Width) != value)
864                 {
865                     ThrowException($"Tried to set {Width}-bit signal to 0x{value:X}");
866                 }
867                 destination = value;
868             }
869 
ThrowException(string message)870             private void ThrowException(string message)
871             {
872                 throw new RecoverableException($"{parent.GetName()}: {Name}: {message}");
873             }
874 
875             private ulong resetValue;
876             private ulong value;
877             private uint width;
878 
879             private readonly IPeripheral parent;
880         }
881 
882         private class SignalWithImmediateEffect<TEnum> : Signal<TEnum>
883             where TEnum: struct
884         {
CreateInput(IPeripheral parent, TEnum signal, uint width, Action<ulong, ulong> setter, bool callSetterAtInitAndReset = false, ulong resetValue = 0)885             public static SignalWithImmediateEffect<TEnum> CreateInput(IPeripheral parent, TEnum signal, uint width,
886                             Action<ulong, ulong> setter, bool callSetterAtInitAndReset = false, ulong resetValue = 0)
887             {
888                 if(setter == null)
889                 {
890                     throw new ConstructionException("Setter cannot be null for input signal with immediate effect.");
891                 }
892                 return new SignalWithImmediateEffect<TEnum>(parent, signal, width, null, setter, callSetterAtInitAndReset, resetValue);
893             }
894 
CreateOutput(IPeripheral parent, TEnum signal, uint width, Func<ulong, ulong> getter)895             public static SignalWithImmediateEffect<TEnum> CreateOutput(IPeripheral parent, TEnum signal, uint width, Func<ulong, ulong> getter)
896             {
897                 if(getter == null)
898                 {
899                     throw new ConstructionException("Getter cannot be null for output signal with immediate effect.");
900                 }
901                 return new SignalWithImmediateEffect<TEnum>(parent, signal, width, getter);
902             }
903 
SignalWithImmediateEffect(IPeripheral parent, TEnum signal, uint width, Func<ulong, ulong> getter = null, Action<ulong, ulong> setter = null, bool callSetterAtInitAndReset = false, ulong resetValue = 0)904             private SignalWithImmediateEffect(IPeripheral parent, TEnum signal, uint width, Func<ulong, ulong> getter = null,
905                             Action<ulong, ulong> setter = null, bool callSetterAtInitAndReset = false, ulong resetValue = 0)
906                             : base(parent, signal, width, resetValue)
907             {
908                 if(getter != null && setter != null)
909                 {
910                     throw new ConstructionException("Signal cannot have both getter and setter");
911                 }
912                 this.callSetterAtInitAndReset = callSetterAtInitAndReset;
913                 this.getter = getter;
914                 this.setter = setter;
915 
916                 Reset();
917             }
918 
Reset()919             public override void Reset()
920             {
921                 var oldValue = Value;
922                 base.Reset();
923 
924                 if(callSetterAtInitAndReset)
925                 {
926                     setter?.Invoke(oldValue, Value);
927                 }
928             }
929 
930             public override ulong Value
931             {
932                 get
933                 {
934                     return getter?.Invoke(base.Value) ?? base.Value;
935                 }
936 
937                 set
938                 {
939                     var oldValue = base.Value;
940                     base.Value = value;
941                     setter?.Invoke(oldValue, value);
942                 }
943             }
944 
945             private readonly bool callSetterAtInitAndReset;
946             private readonly Func<ulong, ulong> getter;
947             private readonly Action<ulong, ulong> setter;
948         }
949     }
950 
951     public enum ArmSignals
952     {
953         DebugROMAddress,
954         DebugROMAddressValid,
955         DebugSelfAddress,
956         DebugSelfAddressValid,
957         HighExceptionVectors,
958         InitializeInstructionTCM,
959         MasterFilterEnable,
960         MasterFilterEnd,
961         MasterFilterStart,
962         PeripheralsBase,
963         PeripheralFilterEnd,
964         PeripheralFilterStart,
965 
966         // Cortex-R5 AHB/AXI peripheral interface region signals based on:
967         //   https://developer.arm.com/documentation/ddi0460/d/Signal-Descriptions/Configuration-signals
968         // There's no "enabled out-of-reset" signal for virtual AXI peripheral interface
969         AHBBaseAddress,
970         AHBInitEnabled,
971         AHBSize,
972         AXIBaseAddress,
973         AXIInitEnabled,
974         AXISize,
975         VirtualAXIBaseAddress,
976         VirtualAXISize,
977     }
978 }
979