1 // 2 // Copyright (c) 2010-2023 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 Antmicro.Renode.Core; 11 using Antmicro.Renode.Core.Structure.Registers; 12 using Antmicro.Renode.Exceptions; 13 using Antmicro.Renode.Logging; 14 using Antmicro.Renode.Peripherals.Bus; 15 using Antmicro.Renode.Utilities; 16 using Antmicro.Renode.Utilities.Collections; 17 18 namespace Antmicro.Renode.Peripherals.GPIOPort 19 { 20 [AllowedTranslations(AllowedTranslation.ByteToDoubleWord)] 21 public class MiV_CoreGPIO : BaseGPIOPort, IDoubleWordPeripheral, IKnownSize 22 { MiV_CoreGPIO(IMachine machine)23 public MiV_CoreGPIO(IMachine machine) : base(machine, NumberOfInterrupts) 24 { 25 innerLock = new object(); 26 IRQ = new GPIO(); 27 fixedDirection = new bool[NumberOfConnections]; 28 fixedIrqTriggerType = new bool[NumberOfConnections]; 29 30 irqManager = new GPIOInterruptManager(IRQ, State); 31 32 var registersMap = new Dictionary<long, DoubleWordRegister> 33 { 34 {(long)Registers.InterruptClearRegister, new DoubleWordRegister(this).WithValueField(0, 32, FieldMode.Write, 35 writeCallback: (_, value) => 36 { 37 lock(innerLock) 38 { 39 foreach(var i in BitHelper.GetSetBits(value)) 40 { 41 irqManager.ClearInterrupt(i); 42 } 43 } 44 })}, 45 46 {(long)Registers.InputRegister, new DoubleWordRegister(this).WithValueField(0, 32, FieldMode.Read, 47 valueProviderCallback: _ => 48 { 49 lock(innerLock) 50 { 51 return GetConnectedInputPinsState(); 52 } 53 })}, 54 55 {(long)Registers.OutputRegister, new DoubleWordRegister(this).WithValueField(0, 32, 56 writeCallback: (_, value) => 57 { 58 lock(innerLock) 59 { 60 var bits = BitHelper.GetBits((uint)value); 61 for(var i = 0; i < bits.Length; i++) 62 { 63 if((irqManager.PinDirection[i] & GPIOInterruptManager.Direction.Output) != 0) 64 { 65 Connections[i].Set(bits[i]); 66 } 67 } 68 } 69 }, valueProviderCallback: _ => 70 { 71 lock(innerLock) 72 { 73 return GetConnectedOutputPinsState(); 74 } 75 })} 76 }; 77 78 intTypeToVal = new TwoWayDictionary<GPIOInterruptManager.InterruptTrigger, uint>(); 79 intTypeToVal.Add(GPIOInterruptManager.InterruptTrigger.ActiveHigh, 0); 80 intTypeToVal.Add(GPIOInterruptManager.InterruptTrigger.ActiveLow, 1); 81 intTypeToVal.Add(GPIOInterruptManager.InterruptTrigger.RisingEdge, 2); 82 intTypeToVal.Add(GPIOInterruptManager.InterruptTrigger.FallingEdge, 3); 83 intTypeToVal.Add(GPIOInterruptManager.InterruptTrigger.BothEdges, 4); 84 85 for(var i = 0; i < NumberOfInterrupts; i++) 86 { 87 var j = i; 88 registersMap.Add((long)Registers.ConfigurationRegisterBase + i * 0x4, new DoubleWordRegister(this) 89 .WithFlag(0, 90 writeCallback: (_, v) => 91 { 92 if(fixedDirection[j]) 93 { 94 this.Log(LogLevel.Warning, "Cannot change pin #{0} direction because it is fixed"); 95 return; 96 } 97 98 if(v) 99 { 100 irqManager.PinDirection[j] |= GPIOInterruptManager.Direction.Output; 101 } 102 else 103 { 104 irqManager.PinDirection[j] &= ~GPIOInterruptManager.Direction.Output; 105 } 106 }, 107 valueProviderCallback: _ => 108 { 109 return (irqManager.PinDirection[j] & GPIOInterruptManager.Direction.Output) != 0; 110 }, name: "OUTREG") 111 .WithFlag(1, 112 writeCallback: (_, value) => 113 { 114 if(fixedDirection[j]) 115 { 116 this.Log(LogLevel.Warning, "Cannot change pin #{0} direction because it is fixed"); 117 return; 118 } 119 120 if(value) 121 { 122 irqManager.PinDirection[j] |= GPIOInterruptManager.Direction.Input; 123 } 124 else 125 { 126 irqManager.PinDirection[j] &= ~GPIOInterruptManager.Direction.Input; 127 } 128 }, 129 valueProviderCallback: _ => 130 { 131 return (irqManager.PinDirection[j] & GPIOInterruptManager.Direction.Input) != 0; 132 }, name: "INREG") 133 .WithFlag(2, name: "OUTBUFF") // The register only provides a read-back function 134 .WithFlag(3, writeCallback: (_, v) => irqManager.InterruptEnable[j] = v, valueProviderCallback: _ => irqManager.InterruptEnable[j], name: "INTENABLE") 135 .WithReservedBits(4, 1) 136 .WithValueField(5, 3, writeCallback: (_, value) => 137 { 138 if(fixedIrqTriggerType[j]) 139 { 140 this.Log(LogLevel.Warning, "Cannot change pin #{0} interrupt type because it is fixed"); 141 return; 142 } 143 144 if(!intTypeToVal.TryGetValue((uint)value, out var type)) 145 { 146 this.Log(LogLevel.Warning, "Invalid interrupt type for pin #{0}: {1}", j, value); 147 return; 148 } 149 irqManager.InterruptType[j] = type; 150 }, valueProviderCallback: _ => 151 { 152 if(!intTypeToVal.TryGetValue(irqManager.InterruptType[j], out var value)) 153 { 154 throw new ArgumentOutOfRangeException($"Unknown interrupt trigger type: {irqManager.InterruptType[j]}"); 155 } 156 return value; 157 }, name: "INTTYPE")); 158 } 159 160 registers = new DoubleWordRegisterCollection(this, registersMap); 161 } 162 ReadDoubleWord(long offset)163 public uint ReadDoubleWord(long offset) 164 { 165 return registers.Read(offset); 166 } 167 WriteDoubleWord(long offset, uint value)168 public void WriteDoubleWord(long offset, uint value) 169 { 170 registers.Write(offset, value); 171 } 172 OnGPIO(int number, bool value)173 public override void OnGPIO(int number, bool value) 174 { 175 lock(innerLock) 176 { 177 base.OnGPIO(number, value); 178 if((irqManager.PinDirection[number] & GPIOInterruptManager.Direction.Input) == 0) 179 { 180 this.Log(LogLevel.Warning, "Writing to an output GPIO pin #{0}", number); 181 return; 182 } 183 irqManager.RefreshInterrupts(); 184 } 185 } 186 Reset()187 public override void Reset() 188 { 189 lock(innerLock) 190 { 191 base.Reset(); 192 irqManager.Reset(); 193 registers.Reset(); 194 for(var i = 0; i < NumberOfConnections; ++i) 195 { 196 fixedDirection[i] = false; 197 fixedIrqTriggerType[i] = false; 198 } 199 } 200 } 201 ConfigureFixedPinTriggerMode(int number, GPIOInterruptManager.InterruptTrigger triggerType)202 public void ConfigureFixedPinTriggerMode(int number, GPIOInterruptManager.InterruptTrigger triggerType) 203 { 204 if(!intTypeToVal.TryGetValue(triggerType, out var value)) 205 { 206 throw new RecoverableException("Invalid interrupt type for pin #{number}: {type}"); 207 } 208 irqManager.InterruptType[number] = triggerType; 209 fixedIrqTriggerType[number] = true; 210 } 211 ConfigureFixedPinDirection(int number, PinDirection direction)212 public void ConfigureFixedPinDirection(int number, PinDirection direction) 213 { 214 switch(direction) 215 { 216 case PinDirection.Input: 217 irqManager.PinDirection[number] = GPIOInterruptManager.Direction.Input; 218 break; 219 case PinDirection.Output: 220 irqManager.PinDirection[number] = GPIOInterruptManager.Direction.Output; 221 break; 222 case PinDirection.Bidirectional: 223 irqManager.PinDirection[number] = 224 GPIOInterruptManager.Direction.Input | GPIOInterruptManager.Direction.Output; 225 break; 226 default: 227 throw new RecoverableException("Invalid option of GPIO direction: {direction}"); 228 } 229 fixedDirection[number] = true; 230 } 231 232 public GPIO IRQ { get; } 233 234 public long Size => 0xA4; 235 236 public enum PinDirection : int 237 { 238 Input = 0, 239 Output = 1, 240 Bidirectional = 2 241 } 242 GetConnectedInputPinsState()243 private uint GetConnectedInputPinsState() 244 { 245 var pins = irqManager.PinDirection.Select(x => (x & GPIOInterruptManager.Direction.Input) != 0); 246 var result = pins.Zip(State, (pin, state) => pin && state); 247 return BitHelper.GetValueFromBitsArray(result); 248 } 249 GetConnectedOutputPinsState()250 private uint GetConnectedOutputPinsState() 251 { 252 var pins = irqManager.PinDirection.Select(x => (x & GPIOInterruptManager.Direction.Output) != 0); 253 var result = pins.Zip(Connections.Values, (pin, state) => pin && state.IsSet); 254 return BitHelper.GetValueFromBitsArray(result); 255 } 256 257 private readonly GPIOInterruptManager irqManager; 258 private readonly DoubleWordRegisterCollection registers; 259 private readonly TwoWayDictionary<GPIOInterruptManager.InterruptTrigger, uint> intTypeToVal; 260 private readonly bool[] fixedDirection; 261 private readonly bool[] fixedIrqTriggerType; 262 private readonly object innerLock; 263 264 private const int NumberOfInterrupts = 32; 265 private enum Registers : long 266 { 267 ConfigurationRegisterBase = 0x0, 268 InterruptClearRegister = 0x80, 269 InputRegister = 0x90, 270 OutputRegister = 0xA0 271 } 272 } 273 } 274