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.Linq; 9 using System.Collections.Generic; 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Core.Structure.Registers; 12 using Antmicro.Renode.Logging; 13 using Antmicro.Renode.Peripherals.Bus; 14 using Antmicro.Renode.UserInterface; 15 using Antmicro.Renode.Utilities; 16 17 namespace Antmicro.Renode.Peripherals.GPIOPort 18 { 19 public class NXPGPIOPort : BaseGPIOPort, IBusPeripheral 20 { NXPGPIOPort(IMachine machine, int numberOfPins)21 public NXPGPIOPort(IMachine machine, int numberOfPins) : base(machine, numberOfPins) 22 { 23 IRQ = new GPIO(); 24 interruptManager = new GPIOInterruptManager(IRQ, State); 25 DefineGPIORegisters(); 26 DefinePortRegisters(); 27 } 28 29 [ConnectionRegion("gpio")] ReadDoubleWordFromGPIO(long offset)30 public uint ReadDoubleWordFromGPIO(long offset) 31 { 32 lock(locker) 33 { 34 return gpioRegisters.Read(offset); 35 } 36 } 37 38 [ConnectionRegion("gpio")] WriteDoubleWordToGPIO(long offset, uint value)39 public void WriteDoubleWordToGPIO(long offset, uint value) 40 { 41 lock(locker) 42 { 43 gpioRegisters.Write(offset, value); 44 } 45 } 46 47 [ConnectionRegion("port")] ReadDoubleWordFromPORT(long offset)48 public uint ReadDoubleWordFromPORT(long offset) 49 { 50 lock(locker) 51 { 52 return portRegisters.Read(offset); 53 } 54 } 55 56 [ConnectionRegion("port")] WriteDoubleWordToPORT(long offset, uint value)57 public void WriteDoubleWordToPORT(long offset, uint value) 58 { 59 lock(locker) 60 { 61 portRegisters.Write(offset, value); 62 } 63 } 64 OnGPIO(int number, bool value)65 public override void OnGPIO(int number, bool value) 66 { 67 lock(locker) 68 { 69 if(number >= inputDisabled.Length) 70 { 71 this.Log(LogLevel.Error, "Trying to signal GPIO {0:X}, which is out of range (should be lower than {1:X})", number, inputDisabled.Length); 72 return; 73 } 74 if(inputDisabled[number].Value) 75 { 76 return; 77 } 78 base.OnGPIO(number, value); 79 interruptManager.RefreshInterrupts(); 80 } 81 } 82 Reset()83 public override void Reset() 84 { 85 portRegisters.Reset(); 86 gpioRegisters.Reset(); 87 interruptManager.Reset(); 88 IRQ.Unset(); 89 } 90 91 [UiAccessible] PrintCurrentConfiguration()92 public string[,] PrintCurrentConfiguration() 93 { 94 const string notApplicable = "---"; 95 const string disabled = "Disabled"; 96 var result = new Table(); 97 result.AddRow("Pin", "Direction", "State", "Input enabled", "Trigger mode", "Active interrupt"); 98 for(var i = 0; i < NumberOfConnections; i++) 99 { 100 var isInput = interruptManager.PinDirection[i] == GPIOInterruptManager.Direction.Input; 101 result.AddRow( 102 i.ToString(), 103 interruptManager.PinDirection[i].ToString(), 104 isInput ? State[i].ToString() : Connections[i].IsSet.ToString(), 105 isInput ? (!inputDisabled[i].Value).ToString() : notApplicable, 106 isInput 107 ? (interruptManager.InterruptEnable[i] 108 ? interruptManager.InterruptType[i].ToString() 109 : disabled) 110 : notApplicable, 111 isInput ? interruptManager.ActiveInterrupts.ElementAt(i).ToString() : notApplicable 112 ); 113 } 114 return result.ToArray(); 115 } 116 117 public GPIO IRQ { get; } 118 DefinePortRegisters()119 private void DefinePortRegisters() 120 { 121 var registers = new Dictionary<long, DoubleWordRegister> 122 { 123 {(long)Registers.GlobalPinControlLow, new DoubleWordRegister(this) 124 .WithValueField(0, 16, out var globalPinWriteEnableLow, FieldMode.Write, name: "GPWE") //order of fields is relevant 125 .WithValueField(16, 16, FieldMode.Write, writeCallback: (_, value) => GlobalPinControlWrite((uint)value, (uint)globalPinWriteEnableLow.Value, highBits: false, highRegisters: false), name: "GPWE") 126 }, 127 {(long)Registers.GlobalPinControlHigh, new DoubleWordRegister(this) 128 .WithValueField(0, 16, out var globalPinWriteEnableHigh, FieldMode.Write, name: "GPWE") //order of fields is relevant 129 .WithValueField(16, 16, FieldMode.Write, writeCallback: (_, value) => GlobalPinControlWrite((uint)value, (uint)globalPinWriteEnableHigh.Value, highBits: false, highRegisters: true), name: "GPWD") 130 }, 131 {(long)Registers.GlobalInterruptControlLow, new DoubleWordRegister(this) 132 .WithValueField(0, 16, out var globalInterruptWriteEnableLow, FieldMode.Write, name: "GIWE") //order of fields is relevant 133 .WithValueField(16, 16, FieldMode.Write, writeCallback: (_, value) => GlobalPinControlWrite((uint)value, (uint)globalInterruptWriteEnableLow.Value, highBits: true, highRegisters: false), name: "GIWE") 134 }, 135 {(long)Registers.GlobalInterruptControlHigh, new DoubleWordRegister(this) 136 .WithValueField(0, 16, out var globalInterruptWriteEnableHigh, FieldMode.Write, name: "GIWE") //order of fields is relevant 137 .WithValueField(16, 16, FieldMode.Write, writeCallback: (_, value) => GlobalPinControlWrite((uint)value, (uint)globalInterruptWriteEnableHigh.Value, highBits: true, highRegisters: true), name: "GIWD") 138 }, 139 {(long)Registers.InterruptStatusFlag, new DoubleWordRegister(this) 140 .WithFlags(0, NumberOfConnections, FieldMode.Read | FieldMode.WriteOneToClear, valueProviderCallback: (i, _) => interruptManager.ActiveInterrupts.ElementAt(i), writeCallback: (i, _, value) => 141 { 142 if(value) 143 { 144 interruptManager.ClearInterrupt(i); 145 } 146 }, name: "ISF") 147 .WithWriteCallback((_, __) => UpdateInterrupts()) 148 }, 149 {(long)Registers.DigitalFilterEnable, new DoubleWordRegister(this) 150 .WithTag("DFE", 0, 32) 151 }, 152 {(long)Registers.DigitalFilterClock, new DoubleWordRegister(this) 153 .WithTaggedFlag("CS", 0) 154 .WithReservedBits(1, 31) 155 }, 156 {(long)Registers.DigitalFilterWidth, new DoubleWordRegister(this) 157 .WithTag("FILT", 0, 5) 158 .WithReservedBits(5, 27) 159 }, 160 }; 161 162 pinControl = new DoubleWordRegister[NumberOfConnections]; 163 for(var i = 0; i < NumberOfConnections; ++i) 164 { 165 var j = i; 166 pinControl[j] = new DoubleWordRegister(this) 167 .WithTaggedFlag("PE", 0) 168 .WithTaggedFlag("PS", 1) 169 .WithReservedBits(2, 2) 170 .WithTaggedFlag("PFE", 4) 171 .WithReservedBits(5, 1) 172 .WithTaggedFlag("DSE", 6) 173 .WithReservedBits(7, 1) 174 .WithTag("MUX", 8, 3) 175 .WithReservedBits(11, 4) 176 .WithTaggedFlag("LK", 15) 177 .WithEnumField<DoubleWordRegister, InterruptConfiguration>(16, 4, writeCallback: (_, value) => 178 { 179 interruptManager.InterruptEnable[j] = value != InterruptConfiguration.Disabled; 180 interruptManager.InterruptType[j] = CalculateInterruptType(value); 181 UpdateInterrupts(); 182 }, name: "IRQC") 183 .WithReservedBits(20, 4) 184 .WithFlag(24, FieldMode.Read | FieldMode.WriteOneToClear, valueProviderCallback: _ => interruptManager.ActiveInterrupts.ElementAt(j), writeCallback: (_, value) => 185 { 186 if(value) 187 { 188 interruptManager.ClearInterrupt(j); 189 UpdateInterrupts(); 190 } 191 }, name: "ISF") 192 .WithReservedBits(25, 7) 193 ; 194 registers.Add((long)Registers.PinControlRegisterStart + (0x4 * j), pinControl[j]); 195 } 196 197 portRegisters = new DoubleWordRegisterCollection(this, registers); 198 } 199 DefineGPIORegisters()200 private void DefineGPIORegisters() 201 { 202 var registers = new Dictionary<long, DoubleWordRegister> 203 { 204 {(long)GPIORegisters.DataOutput, new DoubleWordRegister(this) 205 .WithValueField(0, NumberOfConnections, 206 valueProviderCallback: _ => GetSetConnectionBits(), 207 writeCallback: (_, value) => { 208 for(byte i = 0; i < NumberOfConnections; i++) 209 { 210 if(interruptManager.PinDirection[i] != GPIOInterruptManager.Direction.Output) 211 { 212 continue; 213 } 214 Connections[i].Set(BitHelper.IsBitSet(value, i)); 215 } 216 }, 217 name: "PDOR") 218 //We could have WithReservedBits depending on NumberOfConnections, 219 //but we'd need to filter for 32 bits used 220 }, 221 {(long)GPIORegisters.SetOutput, new DoubleWordRegister(this) 222 .WithValueField(0, NumberOfConnections, FieldMode.Write, 223 writeCallback: (_, value) => { 224 value = FilterForDirection((uint)value, true); 225 for(byte i = 0; i < NumberOfConnections; i++) 226 { 227 if(BitHelper.IsBitSet(value, i)) 228 { 229 Connections[i].Set(); 230 } 231 } 232 }, 233 name: "PSOR") 234 }, 235 {(long)GPIORegisters.ClearOutput, new DoubleWordRegister(this) 236 .WithValueField(0, NumberOfConnections, FieldMode.Write, 237 writeCallback: (_, value) => { 238 value = FilterForDirection((uint)value, true); 239 for(byte i = 0; i < NumberOfConnections; i++) 240 { 241 if(BitHelper.IsBitSet(value, i)) 242 { 243 Connections[i].Unset(); 244 } 245 } 246 }, 247 name: "PCOR") 248 }, 249 {(long)GPIORegisters.ToggleOutput, new DoubleWordRegister(this) 250 .WithValueField(0, NumberOfConnections, FieldMode.Write, 251 writeCallback: (_, value) => { 252 value = FilterForDirection((uint)value, true); 253 for(byte i = 0; i < NumberOfConnections; i++) 254 { 255 if(BitHelper.IsBitSet(value, i)) 256 { 257 Connections[i].Toggle(); 258 } 259 } 260 }, 261 name: "PTOR") 262 }, 263 {(long)GPIORegisters.DataInput, new DoubleWordRegister(this) 264 .WithValueField(0, NumberOfConnections, FieldMode.Read, 265 valueProviderCallback: _ => FilterForDirection(BitHelper.GetValueFromBitsArray(State), false), 266 name: "PDIR") 267 }, 268 {(long)GPIORegisters.DataDirection, new DoubleWordRegister(this) 269 .WithFlags(0, NumberOfConnections, 270 writeCallback: (i, _, value) => interruptManager.PinDirection[i] = value ? GPIOInterruptManager.Direction.Output : GPIOInterruptManager.Direction.Input, 271 valueProviderCallback: (i, _) => interruptManager.PinDirection[i] == GPIOInterruptManager.Direction.Output, 272 name: "PDDR") 273 .WithWriteCallback((_, __) => interruptManager.RefreshInterrupts()) 274 }, 275 {(long)GPIORegisters.InputDisable, new DoubleWordRegister(this) 276 .WithFlags(0, NumberOfConnections, out inputDisabled, name: "PIDR") 277 .WithWriteCallback((_, __) => interruptManager.RefreshInterrupts()) 278 }, 279 }; 280 gpioRegisters = new DoubleWordRegisterCollection(this, registers); 281 } 282 UpdateInterrupts()283 private void UpdateInterrupts() 284 { 285 interruptManager.RefreshInterrupts(); 286 } 287 CalculateInterruptType(InterruptConfiguration type)288 private GPIOInterruptManager.InterruptTrigger CalculateInterruptType(InterruptConfiguration type) 289 { 290 switch(type) 291 { 292 case InterruptConfiguration.InterruptWhenLow: 293 return GPIOInterruptManager.InterruptTrigger.ActiveLow; 294 case InterruptConfiguration.InterruptFallingEdge: 295 return GPIOInterruptManager.InterruptTrigger.FallingEdge; 296 case InterruptConfiguration.InterruptRisingEdge: 297 return GPIOInterruptManager.InterruptTrigger.RisingEdge; 298 case InterruptConfiguration.InterruptEitherEdge: 299 return GPIOInterruptManager.InterruptTrigger.BothEdges; 300 case InterruptConfiguration.InterruptWhenHigh: 301 return GPIOInterruptManager.InterruptTrigger.ActiveHigh; 302 case InterruptConfiguration.Disabled: 303 // we have to return something, so we return ActiveLow - but it should not be relevant 304 return GPIOInterruptManager.InterruptTrigger.ActiveLow; 305 default: 306 this.Log(LogLevel.Error, "Unsupported interrupt configuration: {0}", type); 307 return GPIOInterruptManager.InterruptTrigger.ActiveLow; 308 } 309 } 310 GlobalPinControlWrite(uint value, uint whichRegisters, bool highBits, bool highRegisters)311 private void GlobalPinControlWrite(uint value, uint whichRegisters, bool highBits, bool highRegisters) 312 { 313 var firstRegister = highRegisters ? 16 : 0; 314 var firstBit = highBits ? 16 : 0; 315 for(var i = firstRegister; i < Math.Min(16 + firstRegister, NumberOfConnections); i++) 316 { 317 if(!BitHelper.IsBitSet(whichRegisters, (byte)(i - firstRegister))) 318 { 319 continue; 320 } 321 var currentValue = pinControl[i].Read(); 322 BitHelper.SetMaskedValue(ref currentValue, value, firstBit, 16); 323 pinControl[i].Write((long)(Registers.PinControlRegisterStart + 0x4 * i), currentValue); 324 } 325 } 326 FilterForDirection(uint value, bool output)327 private uint FilterForDirection(uint value, bool output) 328 { 329 var mask = BitHelper.GetValueFromBitsArray(interruptManager.PinDirection.Select(x => x == GPIOInterruptManager.Direction.Output)); 330 if(!output) 331 { 332 mask = ~mask & ~BitHelper.GetValueFromBitsArray(inputDisabled.Select(x => x.Value)); 333 } 334 335 return value & mask; 336 } 337 338 private DoubleWordRegisterCollection gpioRegisters; 339 private DoubleWordRegisterCollection portRegisters; 340 private IFlagRegisterField[] inputDisabled; 341 private DoubleWordRegister[] pinControl; 342 343 private readonly GPIOInterruptManager interruptManager; 344 private readonly object locker = new object(); 345 346 private enum InterruptConfiguration 347 { 348 Disabled = 0, 349 DMARequestRisingEdge = 1, 350 DMARequestFallingEdge = 2, 351 DMARequestEitherEdge = 3, 352 InterruptWhenLow = 8, 353 InterruptRisingEdge = 9, 354 InterruptFallingEdge = 10, 355 InterruptEitherEdge = 11, 356 InterruptWhenHigh = 12, 357 } 358 359 private enum GPIORegisters 360 { 361 DataOutput = 0x00, 362 SetOutput = 0x04, 363 ClearOutput = 0x08, 364 ToggleOutput = 0x0C, 365 DataInput = 0x10, 366 DataDirection = 0x14, 367 InputDisable = 0x18 368 } 369 370 private enum Registers 371 { 372 PinControlRegisterStart = 0x00, 373 PinControlRegisterEnd = 0x7C, 374 GlobalPinControlLow = 0x80, 375 GlobalPinControlHigh = 0x84, 376 GlobalInterruptControlLow = 0x88, 377 GlobalInterruptControlHigh = 0x8C, 378 InterruptStatusFlag = 0xA0, 379 DigitalFilterEnable = 0xC0, 380 DigitalFilterClock = 0xC4, 381 DigitalFilterWidth = 0xC8, 382 } 383 } 384 } 385