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