1 // 2 // Copyright (c) 2010-2022 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 13 namespace Antmicro.Renode.Peripherals.GPIOPort 14 { 15 public class OpenTitan_GPIO : BaseGPIOPort, IDoubleWordPeripheral, IKnownSize 16 { OpenTitan_GPIO(IMachine machine)17 public OpenTitan_GPIO(IMachine machine) : base(machine, numberOfPins) 18 { 19 locker = new object(); 20 IRQ = new GPIO(); 21 FatalAlert = new GPIO(); 22 registers = new DoubleWordRegisterCollection(this, BuildRegisterMap()); 23 interruptRequest = new bool[numberOfPins]; 24 interruptEnabled = new bool[numberOfPins]; 25 directOutputValue = new bool[numberOfPins]; 26 maskedOutputValue = new bool[numberOfPins]; 27 directOutputEnabled = new bool[numberOfPins]; 28 maskedOutputEnabled = new bool[numberOfPins]; 29 interruptEnableRising = new bool[numberOfPins]; 30 interruptEnableFalling = new bool[numberOfPins]; 31 interruptEnableHigh = new bool[numberOfPins]; 32 interruptEnableLow = new bool[numberOfPins]; 33 } 34 Reset()35 public override void Reset() 36 { 37 lock(locker) 38 { 39 base.Reset(); 40 IRQ.Unset(); 41 FatalAlert.Unset(); 42 43 registers.Reset(); 44 for(var i = 0; i < numberOfPins; ++i) 45 { 46 interruptRequest[i] = false; 47 interruptEnabled[i] = false; 48 directOutputValue[i] = false; 49 maskedOutputValue[i] = false; 50 directOutputEnabled[i] = false; 51 maskedOutputEnabled[i] = false; 52 interruptEnableRising[i] = false; 53 interruptEnableFalling[i] = false; 54 interruptEnableHigh[i] = false; 55 interruptEnableLow[i] = false; 56 } 57 } 58 } 59 ReadDoubleWord(long offset)60 public uint ReadDoubleWord(long offset) 61 { 62 lock(locker) 63 { 64 return registers.Read(offset); 65 } 66 } 67 WriteDoubleWord(long offset, uint value)68 public void WriteDoubleWord(long offset, uint value) 69 { 70 lock(locker) 71 { 72 registers.Write(offset, value); 73 } 74 } 75 OnGPIO(int number, bool value)76 public override void OnGPIO(int number, bool value) 77 { 78 if(!CheckPinNumber(number)) 79 { 80 return; 81 } 82 83 lock(locker) 84 { 85 var previousState = GetStateOnInput(number); 86 base.OnGPIO(number, value); 87 88 if(interruptEnabled[number]) 89 { 90 var currentState = GetStateOnInput(number); 91 if(interruptEnableRising[number]) 92 { 93 interruptRequest[number] |= !previousState && currentState; 94 } 95 if(interruptEnableFalling[number]) 96 { 97 interruptRequest[number] |= previousState && !currentState; 98 } 99 } 100 101 UpdateIRQ(); 102 } 103 } 104 105 public long Size => 0x3C; 106 107 public GPIO IRQ { get; } 108 public GPIO FatalAlert { get; } 109 BuildRegisterMap()110 private Dictionary<long, DoubleWordRegister> BuildRegisterMap() 111 { 112 return new Dictionary<long, DoubleWordRegister> 113 { 114 {(long)Registers.InterruptState, new DoubleWordRegister(this) 115 .WithFlags(0, 32, FieldMode.Read | FieldMode.WriteOneToClear, name: "INTR_STATE", 116 valueProviderCallback: (id, _) => interruptRequest[id], 117 writeCallback: (id, _, val) => { if(val) interruptRequest[id] = false; }) 118 .WithWriteCallback((_, __) => UpdateIRQ()) 119 }, 120 {(long)Registers.InterruptEnable, new DoubleWordRegister(this) 121 .WithFlags(0, 32, name: "INTR_ENABLE", 122 valueProviderCallback: (id, _) => interruptEnabled[id], 123 writeCallback: (id, _, val) => { interruptEnabled[id] = val; }) 124 .WithWriteCallback((_, __) => UpdateIRQ()) 125 }, 126 {(long)Registers.InterruptTest, new DoubleWordRegister(this) 127 .WithFlags(0, 32, FieldMode.Write, name: "INTR_TEST", 128 writeCallback: (id, _, val) => { interruptRequest[id] |= val; }) 129 .WithWriteCallback((_, __) => UpdateIRQ()) 130 }, 131 {(long)Registers.AlertTest, new DoubleWordRegister(this) 132 .WithFlag(0, FieldMode.Write, writeCallback: (_, val) => { if(val) FatalAlert.Blink(); }, name: "fatal_fault") 133 .WithIgnoredBits(1, 31) 134 }, 135 {(long)Registers.Input, new DoubleWordRegister(this) 136 .WithFlags(0, 32, FieldMode.Read, name: "DATA_IN", 137 valueProviderCallback: (id, _) => GetStateOnInput(id)) 138 }, 139 {(long)Registers.Output, new DoubleWordRegister(this) 140 .WithFlags(0, 32, name: "DIRECT_OUT", 141 valueProviderCallback: (id, _) => directOutputValue[id], 142 writeCallback: (id, _, val) => { directOutputValue[id] = val; }) 143 .WithWriteCallback((_, __) => UpdateConnections()) 144 }, 145 {(long)Registers.OutputMaskedLower, new DoubleWordRegister(this) 146 .WithFlags(0, 16, name: "MASKED_OUT_LOWER.data", 147 valueProviderCallback: (id, _) => Connections[id].IsSet) 148 .WithFlags(16, 16, FieldMode.Write, name: "MASKED_OUT_LOWER.mask") 149 .WithWriteCallback((_, val) => SetOutputMasked(val, lower: true)) 150 }, 151 {(long)Registers.OutputMaskedUpper, new DoubleWordRegister(this) 152 .WithFlags(0, 16, name: "MASKED_OUT_UPPER.data", 153 valueProviderCallback: (id, _) => Connections[id + 16].IsSet) 154 .WithFlags(16, 16, FieldMode.Write, name: "MASKED_OUT_UPPER.mask") 155 .WithWriteCallback((_, val) => SetOutputMasked(val, lower: false)) 156 }, 157 {(long)Registers.OutputEnable, new DoubleWordRegister(this) 158 .WithFlags(0, 32, name: "DIRECT_OE", 159 valueProviderCallback: (id, _) => directOutputEnabled[id], 160 writeCallback: (id, _, val) => { directOutputEnabled[id] = val; }) 161 .WithWriteCallback((_, __) => UpdateConnections()) 162 }, 163 {(long)Registers.OutputEnableMaskedLower, new DoubleWordRegister(this) 164 .WithFlags(0, 16, name: "MASKED_OE_LOWER.data", 165 valueProviderCallback: (id, _) => maskedOutputEnabled[id]) 166 .WithFlags(16, 16, FieldMode.Write, name: "MASKED_OE_LOWER.mask") 167 .WithWriteCallback((_, val) => SetOutputEnableMasked(val, lower: true)) 168 }, 169 {(long)Registers.OutputEnableMaskedUpper, new DoubleWordRegister(this) 170 .WithFlags(0, 16, name: "MASKED_OE_UPPER.data", 171 valueProviderCallback: (id, _) => maskedOutputEnabled[id + 16]) 172 .WithFlags(16, 16, FieldMode.Write, name: "MASKED_OE_UPPER.mask") 173 .WithWriteCallback((_, val) => SetOutputEnableMasked(val, lower: false)) 174 }, 175 {(long)Registers.InterruptEnableRising, new DoubleWordRegister(this) 176 .WithFlags(0, 32, name: "INTR_CTRL_EN_RISING", 177 writeCallback: (id, _, val) => { interruptEnableRising[id] = val; }, 178 valueProviderCallback: (id, _) => interruptEnableRising[id]) 179 }, 180 {(long)Registers.InterruptEnableFalling, new DoubleWordRegister(this) 181 .WithFlags(0, 32, name: "INTR_CTRL_EN_FALLING", 182 writeCallback: (id, _, val) => { interruptEnableFalling[id] = val; }, 183 valueProviderCallback: (id, _) => interruptEnableFalling[id]) 184 }, 185 {(long)Registers.InterruptEnableHigh, new DoubleWordRegister(this) 186 .WithFlags(0, 32, name: "INTR_CTRL_EN_LVLHIGH", 187 writeCallback: (id, _, val) => { interruptEnableHigh[id] = val; }, 188 valueProviderCallback: (id, _) => interruptEnableHigh[id]) 189 .WithWriteCallback((_, __) => UpdateIRQ()) 190 }, 191 {(long)Registers.InterruptEnableLow, new DoubleWordRegister(this) 192 .WithFlags(0, 32, name: "INTR_CTRL_EN_LVLLOW", 193 writeCallback: (id, _, val) => { interruptEnableLow[id] = val; }, 194 valueProviderCallback: (id, _) => interruptEnableLow[id]) 195 .WithWriteCallback((_, __) => UpdateIRQ()) 196 } 197 }; 198 } 199 UpdateIRQ()200 private void UpdateIRQ() 201 { 202 var flag = false; 203 for(var i = 0; i < numberOfPins; ++i) 204 { 205 if(!interruptEnabled[i]) 206 { 207 continue; 208 } 209 var state = GetStateOnInput(i); 210 interruptRequest[i] |= state ? interruptEnableHigh[i] : interruptEnableLow[i]; 211 flag |= interruptRequest[i]; 212 } 213 IRQ.Set(flag); 214 } 215 UpdateConnections()216 private void UpdateConnections() 217 { 218 for(var i = 0; i < numberOfPins; ++i) 219 { 220 if(directOutputEnabled[i]) 221 { 222 Connections[i].Set(directOutputValue[i]); 223 } 224 else if(maskedOutputEnabled[i]) 225 { 226 Connections[i].Set(maskedOutputValue[i]); 227 } 228 else 229 { 230 Connections[i].Set(false); 231 } 232 } 233 UpdateIRQ(); 234 } 235 SetOutputMasked(uint value, bool lower)236 private void SetOutputMasked(uint value, bool lower) 237 { 238 var offset = lower ? 0 : 16; 239 var data = BitHelper.GetBits(value & 0xFFFF); 240 var mask = BitHelper.GetBits(value >> 16); 241 for(var i = 0; i < 16; ++i) 242 { 243 if(mask[i]) 244 { 245 maskedOutputValue[i + offset] = data[i]; 246 } 247 } 248 UpdateConnections(); 249 } 250 SetOutputEnableMasked(uint value, bool lower)251 private void SetOutputEnableMasked(uint value, bool lower) 252 { 253 var offset = lower ? 0 : 16; 254 var data = BitHelper.GetBits(value & 0xFFFF); 255 var mask = BitHelper.GetBits(value >> 16); 256 for(var i = 0; i < 16; ++i) 257 { 258 if(mask[i]) 259 { 260 directOutputEnabled[i + offset] = data[i]; 261 } 262 } 263 UpdateConnections(); 264 } 265 GetStateOnInput(int i)266 private bool GetStateOnInput(int i) 267 { 268 // Output and Input are physically connected 269 return State[i] || Connections[i].IsSet; 270 } 271 272 private readonly DoubleWordRegisterCollection registers; 273 private readonly object locker; 274 private bool[] interruptRequest; 275 private bool[] interruptEnabled; 276 private bool[] directOutputValue; 277 private bool[] maskedOutputValue; 278 private bool[] directOutputEnabled; 279 private bool[] maskedOutputEnabled; 280 private bool[] interruptEnableRising; 281 private bool[] interruptEnableFalling; 282 private bool[] interruptEnableHigh; 283 private bool[] interruptEnableLow; 284 285 private const int numberOfPins = 32; 286 287 private enum Registers : long 288 { 289 InterruptState = 0x0, 290 InterruptEnable = 0x4, 291 InterruptTest = 0x8, 292 AlertTest = 0xC, 293 Input = 0x10, 294 Output = 0x14, 295 OutputMaskedLower = 0x18, 296 OutputMaskedUpper = 0x1C, 297 OutputEnable = 0x20, 298 OutputEnableMaskedLower = 0x24, 299 OutputEnableMaskedUpper = 0x28, 300 InterruptEnableRising = 0x2C, 301 InterruptEnableFalling = 0x30, 302 InterruptEnableHigh = 0x34, 303 InterruptEnableLow = 0x38, 304 InputFilter = 0x3C 305 } 306 } 307 } 308