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.Linq; 9 using System.Collections.Generic; 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Core.Structure.Registers; 12 using Antmicro.Renode.Logging; 13 using Antmicro.Renode.Peripherals.CPU; 14 using Antmicro.Renode.Utilities; 15 using Antmicro.Renode.Exceptions; 16 17 namespace Antmicro.Renode.Peripherals.Miscellaneous 18 { 19 // Implementation based on: https://developer.arm.com/documentation/ddi0458/c/Multiprocessing/About-multiprocessing-and-the-SCU 20 public class ArmSnoopControlUnit : BasicDoubleWordPeripheral, IHasOwnLife, IKnownSize 21 { ArmSnoopControlUnit(IMachine machine, byte smpMask = 0xFF, ArmSignalsUnit signalsUnit = null)22 public ArmSnoopControlUnit(IMachine machine, byte smpMask = 0xFF, ArmSignalsUnit signalsUnit = null) : base(machine) 23 { 24 this.signalsUnit = signalsUnit; 25 this.smpMask = smpMask; 26 registeredCPUs = new List<ICPU>(); 27 28 DefineRegisters(); 29 Reset(); 30 } 31 RegisterCPU(ICPU cpu)32 public void RegisterCPU(ICPU cpu) 33 { 34 if(registeredCPUs.Contains(cpu)) 35 { 36 throw new RecoverableException($"CPU: {cpu.GetName()} was already registered"); 37 } 38 else if(registeredCPUs.Count >= MaximumCPUs) 39 { 40 throw new RecoverableException($"Number of registered CPUs cannot be greater than {MaximumCPUs}"); 41 } 42 else 43 { 44 Logger.Log(LogLevel.Debug, "Registered CPU {0} at index {1}", cpu.GetName(), registeredCPUs.Count); 45 registeredCPUs.Add(cpu); 46 } 47 } 48 Pause()49 public void Pause() 50 { 51 // Intentionally left blank. 52 } 53 Reset()54 public override void Reset() 55 { 56 lockedCPUs = new bool[MaximumCPUs]; 57 started = false; 58 59 /* Do not reset FilteringStart/End properties - this is intentional 60 /* they are SoC-specific and once set should not change on Reset 61 */ 62 63 base.Reset(); 64 } 65 Resume()66 public void Resume() 67 { 68 // Intentionally left blank. 69 } 70 Start()71 public void Start() 72 { 73 if(started) 74 { 75 return; 76 } 77 started = true; 78 ApplyConfigurationSignals(); 79 } 80 WriteDoubleWord(long offset, uint value)81 public override void WriteDoubleWord(long offset, uint value) 82 { 83 if(machine.GetSystemBus(this).TryGetCurrentCPU(out var cpu)) 84 { 85 CheckRegisteredCPUs(); 86 var idx = registeredCPUs.IndexOf(cpu); 87 if(idx == -1) 88 { 89 Logger.Log(LogLevel.Error, "CPU {0} is not registered in Snoop Control Unit", cpu.GetName()); 90 } 91 else if(lockedCPUs[idx]) 92 { 93 Logger.Log(LogLevel.Warning, "Tried to write value {0} at offset {1}, but access for CPU {2} has been locked", value, offset, cpu.GetName()); 94 return; 95 } 96 } 97 RegistersCollection.Write(offset, value); 98 } 99 DefineRegisters()100 private void DefineRegisters() 101 { 102 Registers.Control.Define(this) 103 .WithFlag(0, out enabled, name: "SCU Enable") 104 .WithFlag(1, out addressFilteringEnabled, FieldMode.Read, name: "Address Filtering Enable") 105 .WithFlag(2, FieldMode.Read, valueProviderCallback: (_) => false, name: "ECC/Parity Enable") // Parity for Cortex-A9, otherwise ECC 106 .WithTaggedFlag("Speculative linefills enable", 3) 107 .WithTaggedFlag("Force all Device to port0 enable", 4) // Cortex-A9 only 108 .WithTaggedFlag("Standby enable", 5) 109 .WithTaggedFlag("IC Standby enable", 6) 110 .WithReservedBits(7, 5) 111 .WithTaggedFlag("ECC enable M0", 12) 112 .WithTaggedFlag("ECC enable M1", 13) 113 .WithTaggedFlag("ECC enable MP", 14) 114 .WithTaggedFlag("ECC enable ACP", 15) 115 .WithTaggedFlag("ECC enable 0 FPP", 16) // Cortex-R8 only 116 .WithTaggedFlag("ECC enable 1 FPP", 17) 117 .WithTaggedFlag("ECC enable 2 FPP", 18) 118 .WithTaggedFlag("ECC enable 3 FPP", 19) 119 .WithTaggedFlag("ECC enable AXI TCM slave", 20) 120 .WithReservedBits(21, 11); 121 122 Registers.Configuration.Define(this) 123 // 0 means one processor, 1 means two, etc. 124 .WithValueField(0, 2, FieldMode.Read, valueProviderCallback: (_) => { return (ulong)CountCPUs() - 1; }, name: "Number of Processors") 125 .WithReservedBits(2, 2) 126 /* TODO: To check if a CPU is in SMP mode we should read SMP bit (6) from Auxiliary Control Register 127 For now let's mark all as participating in SMP, if the mask allows it 128 */ 129 .WithValueField(4, 4, FieldMode.Read, 130 valueProviderCallback: (_) => 131 { 132 return BitHelper.CalculateMask(CountCPUs(), 0) & smpMask; 133 }, 134 name: "CPUs in SMP") 135 .WithTag("Cache/Tag RAM sizes", 8, 16) // This field's layout differs greatly between cores 136 .WithReservedBits(24, 7) 137 .WithFlag(31, FieldMode.Read, valueProviderCallback: (_) => 138 { 139 return MasterFilteringStartRange != 0 && MasterFilteringEndRange != 0; 140 }, 141 name: "AXI master port 1"); // Cortex-R8 only 142 143 Registers.AccessControl.Define(this) 144 .WithFlags(0, MaximumCPUs, 145 writeCallback: (idx, _, val) => 146 { 147 if(!val) 148 { 149 lockedCPUs[idx] = true; 150 } 151 }, 152 valueProviderCallback: (idx, _) => !lockedCPUs[idx]) 153 .WithReservedBits(MaximumCPUs, 32 - MaximumCPUs); 154 155 /* We will return 1s here, meaning we don't support limiting access to other peripherals 156 in Secure/Non-secure state 157 */ 158 Registers.NonSecureAccessControl.Define(this) 159 .WithFlags(0, 12, FieldMode.Read, valueProviderCallback: (_, __) => true) 160 .WithReservedBits(12, 20); 161 162 // These should have reserved lower 20 bits (Should-Be-Zero), so we clear them 163 Registers.PeripheralsFilteringStart.Define(this) 164 .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => PeripheralsFilteringStartRange & ~0xFFFFFUL); 165 166 Registers.PeripheralsFilteringEnd.Define(this) 167 .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => PeripheralsFilteringEndRange & ~0xFFFFFUL); 168 169 Registers.MasterFilteringStart.Define(this) 170 .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => MasterFilteringStartRange & ~0xFFFFFUL); 171 172 Registers.MasterFilteringEnd.Define(this) 173 .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => MasterFilteringEndRange & ~0xFFFFFUL); 174 } 175 176 public bool IsPaused => false; 177 178 public ulong MasterFilteringStartRange { get; set; } 179 public ulong MasterFilteringEndRange { get; set; } 180 181 public ulong PeripheralsFilteringStartRange { get; set; } 182 public ulong PeripheralsFilteringEndRange { get; set; } 183 184 public long Size => 0x100; 185 ApplyConfigurationSignals()186 private void ApplyConfigurationSignals() 187 { 188 if(signalsUnit == null) 189 { 190 return; 191 } 192 193 addressFilteringEnabled.Value = signalsUnit.IsSignalEnabled(ArmSignals.MasterFilterEnable); 194 MasterFilteringEndRange = signalsUnit.GetAddress(ArmSignals.MasterFilterEnd); 195 MasterFilteringStartRange = signalsUnit.GetAddress(ArmSignals.MasterFilterStart); 196 PeripheralsFilteringEndRange = signalsUnit.GetAddress(ArmSignals.PeripheralFilterEnd); 197 PeripheralsFilteringStartRange = signalsUnit.GetAddress(ArmSignals.PeripheralFilterStart); 198 } 199 CountCPUs()200 private int CountCPUs() 201 { 202 CheckRegisteredCPUs(); 203 return registeredCPUs.Count; 204 } 205 CheckRegisteredCPUs()206 private void CheckRegisteredCPUs() 207 { 208 if(registeredCPUs.Count == 0) 209 { 210 var cpus = sysbus.GetCPUs(); 211 var numberOfCPUs = cpus.Count(); 212 213 // Cap Number of CPUs 214 if(numberOfCPUs > MaximumCPUs) 215 { 216 Logger.Log(LogLevel.Error, "Number of CPUs: {0} is more than the maximum supported. Capping CPUs number to: {1}. CPUs can be registered manually to overcome this warning.", numberOfCPUs, MaximumCPUs); 217 numberOfCPUs = MaximumCPUs; 218 } 219 220 registeredCPUs = cpus.OrderBy(x => x.MultiprocessingId).Take(numberOfCPUs).ToList(); 221 } 222 } 223 224 private IFlagRegisterField addressFilteringEnabled; 225 private IFlagRegisterField enabled; 226 private List<ICPU> registeredCPUs; 227 private bool[] lockedCPUs; 228 private bool started; 229 230 private readonly ArmSignalsUnit signalsUnit; 231 private readonly byte smpMask; 232 233 // Should not be more than 4, but could be less 234 private const int MaximumCPUs = 4; 235 236 private enum Registers : long 237 { 238 Control = 0x0, 239 Configuration = 0x4, 240 241 // Not implemented 242 PowerStatus = 0x8, 243 InvalidateAllDataCacheLines = 0xC, 244 // Linux might use this due to a bug in cache maintenance operations. Unimplemented 245 SCUDiagnosticControl = 0x30, 246 247 /* See: https://developer.arm.com/documentation/ddi0458/c/Functional-Description/Processor-ports/AXI-master-port-1?lang=en 248 MasterFilteringStart/End forces accesses at the range to be performed through AXI master port 1 which has 249 "ECC protection on data and parity on control bits". These seem to be handled opaquely by the hardware. 250 They can be declared via appropriate properties. 251 */ 252 MasterFilteringStart = 0x40, 253 MasterFilteringEnd = 0x44, 254 255 /* See: https://developer.arm.com/documentation/ddi0458/c/Functional-Description/Processor-ports/AXI-peripheral-port?lang=en 256 "It is used to access certain peripherals with a low latency, and having burst support." 257 On Cortex-R8 these can also be referred to as LLPFilteringStart/End 258 */ 259 PeripheralsFilteringStart = 0x48, 260 PeripheralsFilteringEnd = 0x4C, 261 262 AccessControl = 0x50, 263 264 // See: https://developer.arm.com/documentation/ddi0407/e/snoop-control-unit/scu-registers/scu-non-secure-access-control-register 265 NonSecureAccessControl = 0x54, 266 267 // ECC Error banks are unimplemented 268 ErrorBankFirstEntry = 0x60, 269 ErrorBankSecondEntry = 0x64, 270 271 // Debug operations are unimplemented 272 DebugTagRAMOperation = 0x70, 273 DebugTagRAMDataValue = 0x74, 274 DebugTagRAMECCChunk = 0x78, 275 276 // Cortex-R8 specific - unimplemented 277 FatalECCError = 0x7C, 278 /* Filtering for Fast Peripheral Port 279 We ignore it, as is the case with AXI port 0 280 */ 281 FPPFilteringStartCore0 = 0x80, 282 FPPFilteringEndCore0 = 0x84, 283 FPPFilteringStartCore1 = 0x88, 284 FPPFilteringEndCore1 = 0x8C, 285 FPPFilteringStartCore2 = 0x90, 286 FPPFilteringEndCore2 = 0x94, 287 FPPFilteringStartCore3 = 0x98, 288 FPPFilteringEndCore3 = 0x9C, 289 } 290 } 291 } 292