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