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;
9 using System.Collections.Generic;
10 using System.Linq;
11 using Antmicro.Renode.Core;
12 using Antmicro.Renode.Utilities.Collections;
13 
14 namespace Antmicro.Renode.Peripherals.GPIOPort
15 {
16     public class GPIOInterruptManager
17     {
GPIOInterruptManager(GPIO irq, ArraySegment<bool> state)18         public GPIOInterruptManager(GPIO irq, ArraySegment<bool> state)
19         {
20             this.numberOfGpios = (uint)state.Count;
21             this.underlyingIrq = irq;
22             this.underlyingState = state;
23 
24             interruptEnable = new EventRisingCollection<bool>(numberOfGpios, () => RefreshInterrupts());
25             interruptType = new EventRisingCollection<InterruptTrigger>(numberOfGpios, () => RefreshInterrupts());
26             interruptMask = new EventRisingCollection<bool>(numberOfGpios, () => RefreshInterrupts());
27             pinDirection = new EventRisingCollection<Direction>(numberOfGpios, () => RefreshInterrupts());
28 
29             previousState = new bool[numberOfGpios];
30             activeInterrupts = new bool[numberOfGpios];
31         }
32 
GPIOInterruptManager(GPIO irq, bool[] state)33         public GPIOInterruptManager(GPIO irq, bool[] state)
34             : this(irq, new ArraySegment<bool>(state))
35         {
36         }
37 
Reset()38         public void Reset()
39         {
40             interruptEnable.Clear();
41             interruptType.Clear();
42             interruptMask.Clear();
43             pinDirection.Clear();
44 
45             Array.Clear(previousState, 0, previousState.Length);
46             Array.Clear(activeInterrupts, 0, activeInterrupts.Length);
47         }
48 
49         /// <summary>
50         /// Clears the interrupt caused by the GPIO pin number <see argref="index"/>.
51         /// </summary>
52         /// <remarks>
53         /// It is possible that the interrupt will be reissued right after clearing
54         /// if the interrupt condition for the pin is true.
55         /// </remarks>
ClearInterrupt(int index)56         public void ClearInterrupt(int index)
57         {
58             activeInterrupts[index] = false;
59             RefreshInterrupts();
60         }
61 
RefreshInterrupts()62         public void RefreshInterrupts()
63         {
64             var irqState = false;
65             for(var i = 0; i < numberOfGpios; i++)
66             {
67                 if(!InterruptEnable[i] || (pinDirection[i] & Direction.Input) == 0)
68                 {
69                     continue;
70                 }
71 
72                 // Using underlyingState[i] should be possible, but it won't compile on mono
73                 // Using the underlying array of the the segment can be used as as alternative
74                 var currentState = underlyingState.Array[underlyingState.Offset + i];
75 
76                 var isEdge = currentState != previousState[i];
77                 switch(InterruptType[i])
78                 {
79                     case InterruptTrigger.ActiveHigh:
80                         if(DeassertActiveInterruptTrigger)
81                         {
82                             activeInterrupts[i] = currentState;
83                         }
84                         else
85                         {
86                             activeInterrupts[i] |= currentState;
87                         }
88                         irqState |= activeInterrupts[i] && !InterruptMask[i];
89                         break;
90                     case InterruptTrigger.ActiveLow:
91                         if(DeassertActiveInterruptTrigger)
92                         {
93                             activeInterrupts[i] = !currentState;
94                         }
95                         else
96                         {
97                             activeInterrupts[i] |= !currentState;
98                         }
99                         irqState |= activeInterrupts[i] && !InterruptMask[i];
100                         break;
101                     case InterruptTrigger.RisingEdge:
102                         if(isEdge && currentState)
103                         {
104                             irqState |= !InterruptMask[i];
105                             activeInterrupts[i] = true;
106                         }
107                         break;
108                     case InterruptTrigger.FallingEdge:
109                         if(isEdge && !currentState)
110                         {
111                             irqState |= !InterruptMask[i];
112                             activeInterrupts[i] = true;
113                         }
114                         break;
115                     case InterruptTrigger.BothEdges:
116                         if(isEdge)
117                         {
118                             irqState |= !InterruptMask[i];
119                             activeInterrupts[i] = true;
120                         }
121                         break;
122                 }
123             }
124             Array.ConstrainedCopy(underlyingState.Array, underlyingState.Offset, previousState, 0, underlyingState.Count);
125             if(irqState)
126             {
127                 underlyingIrq.Set();
128             }
129             else if(!activeInterrupts.Any(x => x))
130             {
131                 underlyingIrq.Unset();
132             }
133         }
134 
135         public bool DeassertActiveInterruptTrigger { get; set; }
136 
137         public IArray<bool> InterruptEnable { get { return interruptEnable; } }
138 
139         public IArray<InterruptTrigger> InterruptType { get { return interruptType; } }
140 
141         public IArray<bool> InterruptMask { get { return interruptMask; } }
142 
143         public IArray<Direction> PinDirection { get { return pinDirection; } }
144 
145         public IReadOnlyCollection<bool> State { get { return underlyingState; } }
146 
147         public IReadOnlyCollection<bool> ActiveInterrupts { get { return activeInterrupts; } }
148 
149         private readonly uint numberOfGpios;
150         private readonly ArraySegment<bool> underlyingState;
151         private readonly bool[] previousState;
152         private readonly bool[] activeInterrupts;
153         private readonly GPIO underlyingIrq;
154 
155         private readonly EventRisingCollection<bool> interruptEnable;
156         private readonly EventRisingCollection<InterruptTrigger> interruptType;
157         private readonly EventRisingCollection<bool> interruptMask;
158         private readonly EventRisingCollection<Direction> pinDirection;
159 
160         public enum InterruptTrigger
161         {
162             ActiveLow,
163             ActiveHigh,
164             FallingEdge,
165             RisingEdge,
166             BothEdges
167         }
168 
169         [Flags]
170         public enum Direction
171         {
172             Input = 0x1,
173             Output = 0x2
174         }
175 
176         public class EventRisingCollection<T> : IArray<T>
177         {
EventRisingCollection(uint size, Action onChanged)178             public EventRisingCollection(uint size, Action onChanged)
179             {
180                 elements = new T[size];
181                 this.onChanged = onChanged;
182             }
183 
GetEnumerator()184             public IEnumerator<T> GetEnumerator()
185             {
186                 return ((IEnumerable<T>)elements).GetEnumerator();
187             }
188 
IEnumerable.GetEnumerator()189             IEnumerator IEnumerable.GetEnumerator()
190             {
191                 return elements.GetEnumerator();
192             }
193 
Clear()194             public void Clear()
195             {
196                 for(var i = 0; i < elements.Length; i++)
197                 {
198                     elements[i] = default(T);
199                 }
200             }
201 
202             public T this[int index]
203             {
204                 get
205                 {
206                     return elements[index];
207                 }
208 
209                 set
210                 {
211                     var currentValue = elements[index];
212                     if(currentValue.Equals(value))
213                     {
214                         return;
215                     }
216 
217                     elements[index] = value;
218                     onChanged();
219                 }
220             }
221 
222             public int Length => elements.Length;
223 
224             private readonly T[] elements;
225             private readonly Action onChanged;
226         }
227     }
228 }
229