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