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 
8 using System.Linq;
9 using System.Collections.Generic;
10 using Antmicro.Renode.Peripherals.CPU;
11 using Antmicro.Renode.Logging;
12 
13 namespace Antmicro.Renode.Peripherals.IRQControllers.PLIC
14 {
15     public class IrqContext
16     {
IrqContext(uint id, IPlatformLevelInterruptController irqController)17         public IrqContext(uint id, IPlatformLevelInterruptController irqController)
18         {
19             this.irqController = irqController;
20             this.id = id;
21 
22             enabledSources = new HashSet<IrqSource>();
23             pendingSources = new HashSet<IrqSource>();
24             activeInterrupts = new Stack<IrqSource>();
25         }
26 
ToString()27         public override string ToString()
28         {
29             return $"[Context #{id}]";
30         }
31 
Reset()32         public void Reset()
33         {
34             activeInterrupts.Clear();
35             enabledSources.Clear();
36 
37             RefreshInterrupt();
38         }
39 
RefreshInterrupt()40         public void RefreshInterrupt()
41         {
42             var forcedContext = irqController.ForcedContext;
43             if(forcedContext != -1 && this.id != forcedContext)
44             {
45                 irqController.Connections[(int)this.id].Set(false);
46                 return;
47             }
48 
49             var currentPriority = activeInterrupts.Count > 0 ? activeInterrupts.Peek().Priority : 0;
50             var isPending = pendingSources.Any(x => x.Priority > currentPriority);
51             irqController.Connections[(int)this.id].Set(isPending);
52         }
53 
CompleteHandlingInterrupt(IrqSource irq)54         public void CompleteHandlingInterrupt(IrqSource irq)
55         {
56             irqController.Log(LogLevel.Noisy, "Completing irq {0} at {1}", irq.Id, this);
57 
58             if(activeInterrupts.Count == 0)
59             {
60                 irqController.Log(LogLevel.Error, "Trying to complete irq {0} @ {1}, there are no active interrupts left", irq.Id, this);
61                 return;
62             }
63             var topActiveInterrupt = activeInterrupts.Pop();
64             if(topActiveInterrupt != irq)
65             {
66                 irqController.Log(LogLevel.Error, "Trying to complete irq {0} @ {1}, but {2} is the active one", irq.Id, this, topActiveInterrupt.Id);
67                 return;
68             }
69 
70             if(irq.State)
71             {
72                 MarkSourceAsPending(irq);
73             }
74             else
75             {
76                 RemovePendingStatusFromSource(irq);
77             }
78             RefreshInterrupt();
79         }
80 
EnableSource(IrqSource s, bool enabled)81         public void EnableSource(IrqSource s, bool enabled)
82         {
83             if(enabled)
84             {
85                 enabledSources.Add(s);
86                 if(s.State)
87                 {
88                     MarkSourceAsPending(s);
89                 }
90             }
91             else
92             {
93                 enabledSources.Remove(s);
94                 RemovePendingStatusFromSource(s);
95             }
96             irqController.Log(LogLevel.Noisy, "{0} source #{1} @ {2}", enabled ? "Enabling" : "Disabling", s.Id, this);
97             RefreshInterrupt();
98         }
99 
MarkSourceAsPending(IrqSource s)100         public void MarkSourceAsPending(IrqSource s)
101         {
102             if(enabledSources.Contains(s))
103             {
104                 if(pendingSources.Add(s))
105                 {
106                     irqController.Log(LogLevel.Noisy, "Setting pending status to True for source #{0}", s.Id);
107                 }
108             }
109         }
110 
AcknowledgePendingInterrupt()111         public uint AcknowledgePendingInterrupt()
112         {
113             IrqSource pendingIrq;
114 
115             var forcedContext = irqController.ForcedContext;
116             if(forcedContext != -1 && this.id != forcedContext)
117             {
118                 pendingIrq = null;
119             }
120             else
121             {
122                 pendingIrq = pendingSources
123                     .OrderByDescending(x => x.Priority)
124                     .ThenBy(x => x.Id).FirstOrDefault();
125             }
126 
127             if(pendingIrq == null)
128             {
129                 irqController.Log(LogLevel.Noisy, "There is no pending interrupt to acknowledge at the moment for {0}. Currently enabled sources: {1}", this, string.Join(", ", enabledSources.Select(x => x.ToString())));
130                 return 0;
131             }
132             RemovePendingStatusFromSource(pendingIrq);
133             activeInterrupts.Push(pendingIrq);
134 
135             irqController.Log(LogLevel.Noisy, "Acknowledging pending interrupt #{0} @ {1}", pendingIrq.Id, this);
136 
137             RefreshInterrupt();
138             return pendingIrq.Id;
139         }
140 
RemovePendingStatusFromSource(IrqSource s)141         private void RemovePendingStatusFromSource(IrqSource s)
142         {
143             if(pendingSources.Remove(s))
144             {
145                 irqController.Log(LogLevel.Noisy, "Setting pending status to False for source #{0}", s.Id);
146             }
147         }
148 
149         private readonly uint id;
150         private readonly IPlatformLevelInterruptController irqController;
151         private readonly HashSet<IrqSource> enabledSources;
152         private readonly HashSet<IrqSource> pendingSources;
153         private readonly Stack<IrqSource> activeInterrupts;
154     }
155 }
156