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.Reflection;
11 using Antmicro.Renode.Core;
12 using Antmicro.Renode.Core.Structure.Registers;
13 using Antmicro.Renode.Logging;
14 using Antmicro.Renode.Peripherals;
15 
16 namespace Antmicro.Renode.Utilities
17 {
18     // it's not possible to limit generic type parameter to enum in C# directly, so we verify it in the constructor
19     public class InterruptManager<TInterrupt> where TInterrupt : struct, IConvertible
20     {
InterruptManager(IPeripheral master, IGPIO irq = null, string gpioName = null, int subvector = -1)21         public InterruptManager(IPeripheral master, IGPIO irq = null, string gpioName = null, int subvector = -1)
22         {
23             if(!typeof(TInterrupt).IsEnum)
24             {
25                 throw new ArgumentException("TInterrupt must be an enum");
26             }
27 
28             this.master = master;
29             activeInterrupts = new HashSet<TInterrupt>();
30             enabledInterrupts = new HashSet<TInterrupt>();
31             subvectors = new Dictionary<IGPIO, HashSet<TInterrupt>>();
32             gpioNames = new Dictionary<IGPIO, string>();
33             nonsettableInterrupts = new HashSet<TInterrupt>();
34             enabledOnResetInterrupts = new HashSet<TInterrupt>();
35 
36             var subvectorIdToGpio = new Dictionary<int, IGPIO>();
37 
38             if(irq != null)
39             {
40                 subvectorIdToGpio.Add(subvector, irq);
41                 gpioNames[irq] = gpioName ?? string.Empty;
42             }
43             else
44             {
45                 // scan for irq providers
46                 foreach(var member in master.GetType().GetProperties())
47                 {
48                     var irqProviderAttribute = (IrqProviderAttribute)member.GetCustomAttributes(typeof(IrqProviderAttribute), false).SingleOrDefault();
49                     if(irqProviderAttribute == null)
50                     {
51                         continue;
52                     }
53 
54                     var field = member.GetMethod.Invoke(master, new object[0]);
55                     if(field == null)
56                     {
57                         throw new ArgumentException("Trying to create the InterruptManager instance, but the IrqProvider object is not initialized");
58                     }
59                     var gpioField = field as IGPIO;
60                     if(gpioField == null)
61                     {
62                         throw new ArgumentException("IrqProviderAttribute can only be used on properties of type IGPIO.");
63                     }
64 
65                     subvectorIdToGpio.Add(irqProviderAttribute.SubvectorId, gpioField);
66                     gpioNames[gpioField] = irqProviderAttribute.Name ?? member.Name;
67                 }
68             }
69 
70             // this iterates over all values of an enum (SpecialName here is to filter out non-value members of enum type)
71             foreach(var member in typeof(TInterrupt).GetFields().Where(x => !x.Attributes.HasFlag(FieldAttributes.SpecialName)))
72             {
73                 var subvectorId = 0;
74                 var subvectorAttribute = member.GetCustomAttributes(false).OfType<SubvectorAttribute>().SingleOrDefault();
75                 var nonsettableAttribute = member.GetCustomAttributes(false).OfType<NotSettableAttribute>().SingleOrDefault();
76                 var enabledOnResetAttribute = member.GetCustomAttributes(false).OfType<EnabledOnResetAttribute>().SingleOrDefault();
77 
78                 if(subvectorAttribute != null)
79                 {
80                     if(!subvectorIdToGpio.ContainsKey(subvectorAttribute.SubvectorId))
81                     {
82                         throw new ArgumentException(string.Format("There is no gpio defined for subvector {0}", subvectorAttribute.SubvectorId));
83                     }
84                     subvectorId = subvectorAttribute.SubvectorId;
85                 }
86                 else
87                 {
88                     if(!subvectorIdToGpio.ContainsKey(-1))
89                     {
90                         throw new ArgumentException("There is no default gpio defined");
91                     }
92                     subvectorId = -1;
93                 }
94 
95                 var gpio = subvectorIdToGpio[subvectorId];
96                 if(!subvectors.TryGetValue(gpio, out HashSet<TInterrupt> interrupts))
97                 {
98                     interrupts = new HashSet<TInterrupt>();
99                     subvectors.Add(gpio, interrupts);
100                 }
101 
102                 var interrupt = (TInterrupt)Enum.Parse(typeof(TInterrupt), member.Name);
103                 interrupts.Add(interrupt);
104                 if(nonsettableAttribute != null)
105                 {
106                     nonsettableInterrupts.Add(interrupt);
107                 }
108 
109                 if(enabledOnResetAttribute != null)
110                 {
111                     enabledOnResetInterrupts.Add(interrupt);
112                 }
113             }
114 
115             Reset();
116         }
117 
Reset()118         public void Reset()
119         {
120             activeInterrupts.Clear();
121             enabledInterrupts.Clear();
122             foreach(var irq in enabledOnResetInterrupts)
123             {
124                 enabledInterrupts.Add(irq);
125             }
126             RefreshInterrupts();
127         }
128 
129         public TRegister GetRegister<TRegister>(Func<TInterrupt, bool, bool> valueProviderCallback = null, Action<TInterrupt, bool, bool> writeCallback = null) where TRegister : PeripheralRegister
130         {
131             var mode = default(FieldMode);
132             if(valueProviderCallback != null)
133             {
134                 mode |= FieldMode.Read;
135             }
136             if(writeCallback != null)
137             {
138                 mode |= FieldMode.Write;
139             }
140 
141             var result = CreateRegister<TRegister>();
142             foreach(TInterrupt interruptType in Enum.GetValues(typeof(TInterrupt)))
143             {
144                 var local = interruptType;
145                 result.DefineFlagField((int)(object)interruptType, mode, name: interruptType.ToString(),
146                     valueProviderCallback: valueProviderCallback == null ? null : (Func<bool, bool>)(oldValue => valueProviderCallback(local, oldValue)),
147                     writeCallback: writeCallback == null ? null : (Action<bool, bool>)((oldValue, newValue) => writeCallback(local, oldValue, newValue)));
148             }
149             return result;
150         }
151 
152         public TRegister GetMaskedInterruptFlagRegister<TRegister>() where TRegister : PeripheralRegister
153         {
154             var result = CreateRegister<TRegister>();
155             foreach(TInterrupt interruptType in Enum.GetValues(typeof(TInterrupt)))
156             {
157                 var local = interruptType;
158                 result.DefineFlagField((int)(object)interruptType, FieldMode.Read, name: interruptType.ToString(),
159                                        valueProviderCallback: _ => IsSet(local)  && IsEnabled(local));
160             }
161             return result;
162         }
163 
164         public TRegister GetRawInterruptFlagRegister<TRegister>() where TRegister : PeripheralRegister
165         {
166             var result = CreateRegister<TRegister>();
167             foreach(TInterrupt interruptType in Enum.GetValues(typeof(TInterrupt)))
168             {
169                 var local = interruptType;
170                 result.DefineFlagField((int)(object)interruptType, FieldMode.Read, name: interruptType.ToString(),
171                                        valueProviderCallback: _ => IsSet(local));
172             }
173             return result;
174         }
175 
176         public TRegister GetInterruptEnableSetRegister<TRegister>() where TRegister : PeripheralRegister
177         {
178             var result = CreateRegister<TRegister>();
179             foreach(TInterrupt interruptType in Enum.GetValues(typeof(TInterrupt)))
180             {
181                 var local = interruptType;
182                 result.DefineFlagField((int)(object)interruptType, name: interruptType.ToString(),
183                                        valueProviderCallback: _ => IsEnabled(local),
184                                        writeCallback: (_, v) => { if(v) EnableInterrupt(local, true); });
185             }
186             return result;
187         }
188 
189         public TRegister GetInterruptEnableClearRegister<TRegister>() where TRegister : PeripheralRegister
190         {
191             var result = CreateRegister<TRegister>();
192             foreach(TInterrupt interruptType in Enum.GetValues(typeof(TInterrupt)))
193             {
194                 var local = interruptType;
195                 result.DefineFlagField((int)(object)interruptType, name: interruptType.ToString(),
196                                        valueProviderCallback: _ => IsEnabled(local),
197                                        writeCallback: (_, v) => { if(v) EnableInterrupt(local, false); });
198             }
199             return result;
200         }
201 
202         public TRegister GetInterruptEnableRegister<TRegister>() where TRegister : PeripheralRegister
203         {
204             var result = CreateRegister<TRegister>();
205             foreach(TInterrupt interruptType in Enum.GetValues(typeof(TInterrupt)))
206             {
207                 var local = interruptType;
208                 result.DefineFlagField((int)(object)interruptType, name: interruptType.ToString(),
209                                        valueProviderCallback: _ => IsEnabled(local),
210                                        writeCallback: (_, v) => EnableInterrupt(local, v));
211             }
212             return result;
213         }
214 
215         public TRegister GetInterruptSetRegister<TRegister>() where TRegister : PeripheralRegister
216         {
217             var result = CreateRegister<TRegister>();
218             foreach(TInterrupt interruptType in Enum.GetValues(typeof(TInterrupt)))
219             {
220                 var local = interruptType;
221                 if(!nonsettableInterrupts.Contains(interruptType))
222                 {
223                     result.DefineFlagField((int)(object)interruptType, FieldMode.Set, name: interruptType.ToString(),
224                                            writeCallback: (_, __) => SetInterrupt(local));
225                 }
226             }
227 
228             return result;
229         }
230 
231         public TRegister GetInterruptClearRegister<TRegister>() where TRegister : PeripheralRegister
232         {
233             var result = CreateRegister<TRegister>();
234             foreach(TInterrupt interruptType in Enum.GetValues(typeof(TInterrupt)))
235             {
236                 var local = interruptType;
237                 if(!nonsettableInterrupts.Contains(interruptType))
238                 {
239                     result.DefineFlagField((int)(object)interruptType, FieldMode.Set, name: interruptType.ToString(),
240                                            writeCallback: (_, __) => ClearInterrupt(local));
241                 }
242             }
243 
244             return result;
245         }
246 
EnableInterrupt(TInterrupt interrupt, bool status = true)247         public void EnableInterrupt(TInterrupt interrupt, bool status = true)
248         {
249             if(status)
250             {
251                 enabledInterrupts.Add(interrupt);
252             }
253             else
254             {
255                 enabledInterrupts.Remove(interrupt);
256             }
257             RefreshInterrupts();
258         }
259 
DisableInterrupt(TInterrupt interrupt)260         public void DisableInterrupt(TInterrupt interrupt)
261         {
262             EnableInterrupt(interrupt, false);
263         }
264 
SetInterrupt(TInterrupt interrupt, bool status = true)265         public void SetInterrupt(TInterrupt interrupt, bool status = true)
266         {
267             if(status)
268             {
269                 activeInterrupts.Add(interrupt);
270             }
271             else
272             {
273                 activeInterrupts.Remove(interrupt);
274             }
275             RefreshInterrupts();
276         }
277 
IsSet(TInterrupt interrupt)278         public bool IsSet(TInterrupt interrupt)
279         {
280             return activeInterrupts.Contains(interrupt);
281         }
282 
IsEnabled(TInterrupt interrupt)283         public bool IsEnabled(TInterrupt interrupt)
284         {
285             return enabledInterrupts.Contains(interrupt);
286         }
287 
ClearInterrupt(TInterrupt interrupt)288         public void ClearInterrupt(TInterrupt interrupt)
289         {
290             SetInterrupt(interrupt, false);
291         }
292 
293         private TRegister CreateRegister<TRegister>() where TRegister : PeripheralRegister
294         {
295             TRegister result = null;
296             if(typeof(TRegister) == typeof(DoubleWordRegister))
297             {
298                 result = (TRegister)(PeripheralRegister)new DoubleWordRegister(master);
299             }
300             else if(typeof(TRegister) == typeof(WordRegister))
301             {
302                 result = (TRegister)(PeripheralRegister)new WordRegister(master);
303             }
304             else if(typeof(TRegister) == typeof(ByteRegister))
305             {
306                 result = (TRegister)(PeripheralRegister)new ByteRegister(master);
307             }
308             return result;
309         }
310 
RefreshInterrupts()311         private void RefreshInterrupts()
312         {
313             foreach(var irq in subvectors)
314             {
315                 var value = enabledInterrupts.Intersect(irq.Value).Intersect(activeInterrupts).Any();
316                 irq.Key.Set(value);
317                 master.Log(LogLevel.Noisy, "{0} set to: {1}", gpioNames[irq.Key], value);
318             }
319         }
320 
321         private readonly HashSet<TInterrupt> enabledOnResetInterrupts;
322         private readonly HashSet<TInterrupt> nonsettableInterrupts;
323         private readonly Dictionary<IGPIO, string> gpioNames;
324         private readonly Dictionary<IGPIO, HashSet<TInterrupt>> subvectors;
325         private readonly HashSet<TInterrupt> activeInterrupts;
326         private readonly HashSet<TInterrupt> enabledInterrupts;
327         private readonly IPeripheral master;
328     }
329 
330     [AttributeUsage(AttributeTargets.Field)]
331     public class SubvectorAttribute : Attribute
332     {
SubvectorAttribute(int subvectorId)333         public SubvectorAttribute(int subvectorId)
334         {
335             SubvectorId = subvectorId;
336         }
337 
338         public int SubvectorId { get; private set; }
339     }
340 
341     [AttributeUsage(AttributeTargets.Field)]
342     public class NotSettableAttribute : Attribute
343     {
344     }
345 
346     [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
347     public class IrqProviderAttribute : Attribute
348     {
IrqProviderAttribute()349         public IrqProviderAttribute()
350         {
351             SubvectorId = -1;
352         }
353 
IrqProviderAttribute(string name, int subvectorId)354         public IrqProviderAttribute(string name, int subvectorId)
355         {
356             if(subvectorId < 0)
357             {
358                 throw new ArgumentException("Subvector id must be non-negative");
359             }
360 
361             Name = name;
362             SubvectorId = subvectorId;
363         }
364 
365         public string Name { get; private set; }
366         public int SubvectorId { get; private set; }
367     }
368 
369     [AttributeUsage(AttributeTargets.Field)]
370     public class EnabledOnResetAttribute : Attribute
371     {
372     }
373 }
374