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 Antmicro.Renode.Core; 8 using Antmicro.Renode.Core.Structure.Registers; 9 using Antmicro.Renode.Exceptions; 10 using Antmicro.Renode.Logging; 11 using Antmicro.Renode.Peripherals.Bus; 12 using System; 13 using System.Collections.Generic; 14 using static Antmicro.Renode.Peripherals.Bus.GaislerAPBPlugAndPlayRecord; 15 16 namespace Antmicro.Renode.Peripherals.GPIOPort 17 { 18 public sealed class Gaisler_GPIO : BaseGPIOPort, IDoubleWordPeripheral, IProvidesRegisterCollection<DoubleWordRegisterCollection>, IKnownSize, IGaislerAPB 19 { 20 // Connections [0; numberOfConnections) are the actual connections. 21 // Connections [numberOfConnections; numberOfConnections + numberOfInterrupts) are the interrupt outputs. Gaisler_GPIO(IMachine machine, int numberOfConnections, int numberOfInterrupts, string inputOnlyPins = R)22 public Gaisler_GPIO(IMachine machine, int numberOfConnections, int numberOfInterrupts, string inputOnlyPins = "") : base(machine, numberOfConnections + numberOfInterrupts) 23 { 24 if(numberOfConnections < 1 || numberOfConnections > 32) 25 { 26 throw new ConstructionException("Number of connections has to be in [1; 32]"); 27 } 28 if(numberOfInterrupts < 1 || numberOfInterrupts > 15) 29 { 30 throw new ConstructionException("Number of interrupts has to be in [1; 15]."); 31 } 32 33 RegistersCollection = new DoubleWordRegisterCollection(this); 34 numberOfActualConnections = numberOfConnections; // NumberOfConnections from BaseGPIOPort also counts the IRQ outputs. 35 this.numberOfInterrupts = numberOfInterrupts; 36 this.inputOnlyPins = new HashSet<int>(); 37 try 38 { 39 foreach(var pin in inputOnlyPins.Split(new [] { ',' }, StringSplitOptions.RemoveEmptyEntries)) 40 { 41 this.inputOnlyPins.Add(int.Parse(pin)); 42 } 43 } 44 catch(Exception e) 45 { 46 throw new ConstructionException($"Failed to parse {nameof(inputOnlyPins)}: {e.Message}", e); 47 } 48 interrupts = new IGPIO[numberOfInterrupts]; 49 for(var i = 0; i < numberOfInterrupts; i++) 50 { 51 interrupts[i] = Connections[numberOfConnections + i]; 52 } 53 54 DefineRegisters(); 55 Reset(); 56 } 57 Reset()58 public override void Reset() 59 { 60 base.Reset(); 61 foreach(var interrupt in interrupts) 62 { 63 interrupt.Unset(); 64 } 65 } 66 OnGPIO(int number, bool value)67 public override void OnGPIO(int number, bool value) 68 { 69 if(!CheckPinNumber(number)) 70 { 71 return; 72 } 73 if(isOutput[number].Value) 74 { 75 this.WarningLog("Writing to GPIO #{0} which is configured as an output", number); 76 return; 77 } 78 79 base.OnGPIO(number, value); 80 if(TryGetInterruptIndex(number, out var it)) 81 { 82 UpdateInterrupt(it); 83 } 84 } 85 86 public long Size => 0x100; 87 88 public DoubleWordRegisterCollection RegistersCollection { get; } 89 ReadDoubleWord(long offset)90 public uint ReadDoubleWord(long offset) 91 { 92 return RegistersCollection.Read(offset); 93 } 94 WriteDoubleWord(long offset, uint value)95 public void WriteDoubleWord(long offset, uint value) 96 { 97 RegistersCollection.Write(offset, value); 98 } 99 GetVendorID()100 public uint GetVendorID() => VendorID; 101 GetDeviceID()102 public uint GetDeviceID() => DeviceID; 103 GetInterruptNumber()104 public uint GetInterruptNumber() => 0; 105 GetSpaceType()106 public SpaceType GetSpaceType() => SpaceType.APBIOSpace; 107 DefineRegisters()108 private void DefineRegisters() 109 { 110 Registers.Data.Define(this) 111 .WithFlags(0, numberOfActualConnections, FieldMode.Read, name: "data", 112 valueProviderCallback: (i, _) => State[i] && !isOutput[i].Value); 113 114 // The GRLIB BSP's GRGPIO driver writes all 1s and reads back the value to determine how many GPIO 115 // lines exist, but afterwards it will write 0s there so we don't define tags with allowed value 1 116 Registers.Output.Define(this) 117 .WithFlags(0, numberOfActualConnections, out outputValue, name: "output", 118 changeCallback: (i, _, value) => UpdateOutput(i)); 119 120 Registers.Direction.Define(this) 121 .WithFlags(0, numberOfActualConnections, out isOutput, name: "direction", 122 changeCallback: (i, _, value) => UpdateOutput(i)); 123 124 Registers.InterruptMask.Define(this) 125 .WithFlags(FirstInterruptPinIndex, numberOfInterrupts, out interruptMask, name: "interruptMask", 126 changeCallback: (i, _, value) => UpdateInterrupt(i)); 127 128 Registers.InterruptPolarity.Define(this) 129 .WithFlags(FirstInterruptPinIndex, numberOfInterrupts, out interruptPolarity, name: "interruptPolarity", 130 changeCallback: (i, _, value) => UpdateInterrupt(i)); 131 132 Registers.InterruptEdge.Define(this) 133 .WithFlags(FirstInterruptPinIndex, numberOfInterrupts, out interruptEdge, name: "interruptEdge", 134 changeCallback: (i, _, value) => UpdateInterrupt(i)); 135 136 Registers.Bypass.Define(this) 137 .WithValueField(0, 32, name: "bypass"); 138 } 139 TryGetInterruptIndex(int pin, out int it)140 private bool TryGetInterruptIndex(int pin, out int it) 141 { 142 it = pin - FirstInterruptPinIndex; 143 return it >= 0 && it < numberOfInterrupts; 144 } 145 UpdateInterrupt(int it)146 private void UpdateInterrupt(int it) 147 { 148 var pin = FirstInterruptPinIndex + it; 149 150 // Output pins cannot trigger interrupts 151 if(isOutput[pin].Value) 152 { 153 return; 154 } 155 156 var interruptState = interruptMask[it].Value && State[pin] == interruptPolarity[it].Value; 157 // For edge-triggered interrupts we blink the output on the appropriate edge 158 if(interruptEdge[it].Value) 159 { 160 if(interruptState) 161 { 162 interrupts[it].Blink(); 163 } 164 } 165 else 166 { 167 interrupts[it].Set(interruptState); 168 } 169 } 170 UpdateOutput(int pin)171 private void UpdateOutput(int pin) 172 { 173 if(!isOutput[pin].Value) 174 { 175 return; 176 } 177 if(inputOnlyPins.Contains(pin)) 178 { 179 this.WarningLog("Ignoring attempt to drive input-only pin #{0}", pin); 180 return; 181 } 182 Connections[pin].Set(outputValue[pin].Value); 183 } 184 185 private const uint VendorID = 0x01; // Gaisler Research 186 private const uint DeviceID = 0x01a; // GRGPIO 187 private const int FirstInterruptPinIndex = 1; 188 private readonly int numberOfInterrupts; 189 private readonly int numberOfActualConnections; 190 private readonly IGPIO[] interrupts; 191 private readonly HashSet<int> inputOnlyPins; 192 193 private IFlagRegisterField[] isOutput; 194 private IFlagRegisterField[] outputValue; 195 private IFlagRegisterField[] interruptMask; 196 private IFlagRegisterField[] interruptPolarity; 197 private IFlagRegisterField[] interruptEdge; 198 199 private enum Registers : uint 200 { 201 Data = 0x00, 202 Output = 0x04, 203 Direction = 0x08, 204 InterruptMask = 0x0c, 205 InterruptPolarity = 0x10, 206 InterruptEdge = 0x14, 207 Bypass = 0x18, 208 } 209 } 210 } 211