1 // 2 // Copyright (c) 2010-2021 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.Collections.Generic; 8 using Antmicro.Renode.Core; 9 using Antmicro.Renode.Utilities; 10 using Antmicro.Renode.Peripherals.Bus; 11 using Antmicro.Renode.Core.Structure.Registers; 12 using Antmicro.Renode.Logging; 13 14 namespace Antmicro.Renode.Peripherals.GPIOPort 15 { 16 public class IMXRT_GPIO : BaseGPIOPort, IDoubleWordPeripheral, IKnownSize 17 { IMXRT_GPIO(IMachine machine)18 public IMXRT_GPIO(IMachine machine) : base(machine, NumberOfPins) 19 { 20 locker = new object(); 21 IRQ = new GPIO(); 22 registers = new DoubleWordRegisterCollection(this, BuildRegisterMap()); 23 data = new bool[NumberOfPins]; 24 directionOutNotIn = new bool[NumberOfPins]; 25 interruptEnabled = new bool[NumberOfPins]; 26 interruptRequest = new bool[NumberOfPins]; 27 edgeSelect = new bool[NumberOfPins]; 28 interruptConfig = new InterruptConfig[NumberOfPins]; 29 } 30 Reset()31 public override void Reset() 32 { 33 lock(locker) 34 { 35 base.Reset(); 36 IRQ.Unset(); 37 registers.Reset(); 38 for(var i = 0; i < NumberOfPins; ++i) 39 { 40 data[i] = false; 41 directionOutNotIn[i] = false; 42 interruptEnabled[i] = false; 43 interruptRequest[i] = false; 44 edgeSelect[i] = false; 45 interruptConfig[i] = InterruptConfig.Low; 46 } 47 } 48 } 49 ReadDoubleWord(long offset)50 public uint ReadDoubleWord(long offset) 51 { 52 lock(locker) 53 { 54 return registers.Read(offset); 55 } 56 } 57 WriteDoubleWord(long offset, uint value)58 public void WriteDoubleWord(long offset, uint value) 59 { 60 lock(locker) 61 { 62 registers.Write(offset, value); 63 } 64 } 65 OnGPIO(int number, bool value)66 public override void OnGPIO(int number, bool value) 67 { 68 if(!CheckPinNumber(number)) 69 { 70 return; 71 } 72 73 if(directionOutNotIn[number]) 74 { 75 this.Log(LogLevel.Warning, "gpio {0} is set to output, signal ignored.", number); 76 return; 77 } 78 79 lock(locker) 80 { 81 var previousState = State[number]; 82 base.OnGPIO(number, value); 83 84 UpdateSingleInterruptRequest(number, value, previousState != value); 85 UpdateIRQ(); 86 } 87 } 88 89 public long Size => 0x90; 90 91 public GPIO IRQ { get; } 92 BuildRegisterMap()93 private Dictionary<long, DoubleWordRegister> BuildRegisterMap() 94 { 95 var registersDictionary = new Dictionary<long, DoubleWordRegister> 96 { 97 {(long)Registers.Data, new DoubleWordRegister(this) 98 .WithFlags(0, NumberOfPins, name: "GR / GPIO data register", 99 writeCallback: (id, _, val) => { data[id] = val; }, 100 valueProviderCallback: (id, _) => 101 { 102 return (directionOutNotIn[id]) 103 ? data[id] 104 : State[id]; 105 }) 106 .WithWriteCallback((_, __) => UpdateConnections()) 107 }, 108 {(long)Registers.Direction, new DoubleWordRegister(this) 109 .WithFlags(0, NumberOfPins, name: "GDIR / GPIO direction register", 110 writeCallback: (id, _, val) => { directionOutNotIn[id] = val; }, 111 valueProviderCallback: (id, _) => directionOutNotIn[id]) 112 .WithWriteCallback((_, __) => UpdateConnections()) 113 }, 114 {(long)Registers.PadStatus, new DoubleWordRegister(this) 115 .WithFlags(0, NumberOfPins, FieldMode.Read, name: "PSR / GPIO pad status register", 116 valueProviderCallback: (id, _) => State[id]) 117 }, 118 {(long)Registers.Mask, new DoubleWordRegister(this) 119 .WithFlags(0, NumberOfPins, name: "IMR / GPIO interrupt mask register", 120 writeCallback: (id, _, val) => { interruptEnabled[id] = val; }, 121 valueProviderCallback: (id, _) => interruptEnabled[id]) 122 .WithWriteCallback((_, __) => UpdateIRQ()) 123 }, 124 {(long)Registers.Status, new DoubleWordRegister(this) 125 .WithFlags(0, NumberOfPins, FieldMode.Read | FieldMode.WriteOneToClear, name: "ISR / GPIO interrupt status register", 126 writeCallback: (id, _, val) => 127 { 128 if(val) 129 { 130 interruptRequest[id] = false; 131 } 132 }, 133 valueProviderCallback: (id, _) => interruptRequest[id]) 134 .WithWriteCallback((_, __) => UpdateIRQ()) 135 }, 136 {(long)Registers.EdgeSelect, new DoubleWordRegister(this) 137 .WithFlags(0, NumberOfPins, name: "EDGE_SEL / GPIO edge select register", 138 writeCallback: (id, _, val) => { edgeSelect[id] = val; }, 139 valueProviderCallback: (id, _) => edgeSelect[id]) 140 }, 141 {(long)Registers.DataSet, new DoubleWordRegister(this) 142 .WithFlags(0, NumberOfPins, FieldMode.Write, name: "DR_SET / GPIO data register SET", 143 writeCallback: (id, _, __) => { data[id] = true; }) 144 .WithWriteCallback((_, __) => UpdateConnections()) 145 }, 146 {(long)Registers.DataClear, new DoubleWordRegister(this) 147 .WithFlags(0, NumberOfPins, FieldMode.Write, name: "DR_CLEAR / GPIO data register CLEAR", 148 writeCallback: (id, _, __) => { data[id] = false; }) 149 .WithWriteCallback((_, __) => UpdateConnections()) 150 }, 151 {(long)Registers.DataToggle, new DoubleWordRegister(this) 152 .WithFlags(0, NumberOfPins, FieldMode.Write, name: "DR_TOGGLE / GPIO data register TOGGLE", 153 writeCallback: (id, _, __) => { data[id] ^= true; }) 154 .WithWriteCallback((_, __) => UpdateConnections()) 155 }, 156 }; 157 158 var config1 = new DoubleWordRegister(this); 159 var config2 = new DoubleWordRegister(this); 160 var half = NumberOfPins / 2; 161 for(var i = 0; i < half; ++i) 162 { 163 var j = i; 164 config1.WithEnumField<DoubleWordRegister, InterruptConfig>(j * 2, 2, 165 name: $"ICR{j} / Interrupt configuration {j}", 166 writeCallback: (_, val) => { interruptConfig[j] = val; }, 167 valueProviderCallback: _ => interruptConfig[j]); 168 config2.WithEnumField<DoubleWordRegister, InterruptConfig>(j * 2, 2, 169 name: $"ICR{half + j} / Interrupt configuration {half + j}", 170 writeCallback: (_, val) => { interruptConfig[half + j] = val; }, 171 valueProviderCallback: _ => interruptConfig[half + j]); 172 } 173 config1.WithWriteCallback((_, __) => UpdateAllInterruptRequests()); 174 config2.WithWriteCallback((_, __) => UpdateAllInterruptRequests()); 175 registersDictionary.Add((long)Registers.Config1, config1); 176 registersDictionary.Add((long)Registers.Config2, config2); 177 return registersDictionary; 178 } 179 UpdateIRQ()180 private void UpdateIRQ() 181 { 182 var flag = false; 183 for(var i = 0; i < NumberOfPins; ++i) 184 { 185 flag |= interruptEnabled[i] && interruptRequest[i]; 186 } 187 IRQ.Set(flag); 188 } 189 UpdateConnections()190 private void UpdateConnections() 191 { 192 for(var i = 0; i < NumberOfPins; ++i) 193 { 194 Connections[i].Set(directionOutNotIn[i] && data[i]); 195 } 196 UpdateIRQ(); 197 } 198 UpdateAllInterruptRequests()199 private void UpdateAllInterruptRequests() 200 { 201 for(var i = 0; i < NumberOfPins; ++i) 202 { 203 UpdateSingleInterruptRequest(i, State[i]); 204 } 205 UpdateIRQ(); 206 } 207 UpdateSingleInterruptRequest(int i, bool currentState, bool stateChanged = false)208 private void UpdateSingleInterruptRequest(int i, bool currentState, bool stateChanged = false) 209 { 210 if(edgeSelect[i]) 211 { 212 interruptRequest[i] |= stateChanged; 213 } 214 else 215 { 216 switch(interruptConfig[i]) 217 { 218 case InterruptConfig.Low: 219 interruptRequest[i] |= !currentState; 220 break; 221 case InterruptConfig.High: 222 interruptRequest[i] |= currentState; 223 break; 224 case InterruptConfig.Rising: 225 interruptRequest[i] |= stateChanged && currentState; 226 break; 227 case InterruptConfig.Falling: 228 interruptRequest[i] |= stateChanged && !currentState; 229 break; 230 default: 231 this.Log(LogLevel.Error, "Invalid state (interruptConfig[{0}]: 0x{1:X}).", i, interruptConfig[i]); 232 break; 233 } 234 } 235 } 236 237 private readonly DoubleWordRegisterCollection registers; 238 private readonly object locker; 239 private readonly bool[] data; 240 private readonly bool[] directionOutNotIn; 241 private readonly bool[] interruptEnabled; 242 private readonly bool[] interruptRequest; 243 private readonly bool[] edgeSelect; 244 private readonly InterruptConfig[] interruptConfig; 245 246 private const int NumberOfPins = 32; 247 248 private enum InterruptConfig 249 { 250 Low = 0b00, 251 High = 0b01, 252 Rising = 0b10, 253 Falling = 0b11, 254 } 255 256 private enum Registers : long 257 { 258 Data = 0x0, 259 Direction = 0x4, 260 PadStatus = 0x8, 261 Config1 = 0xc, 262 Config2 = 0x10, 263 Mask = 0x14, 264 Status = 0x18, 265 EdgeSelect = 0x1c, 266 DataSet = 0x84, 267 DataClear = 0x88, 268 DataToggle = 0x8c, 269 } 270 } 271 } 272