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 System.Collections.Generic; 8 using System.Collections.ObjectModel; 9 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Core.Structure.Registers; 12 using Antmicro.Renode.Logging; 13 using Antmicro.Renode.Utilities; 14 15 namespace Antmicro.Renode.Peripherals.IRQControllers 16 { 17 public class STM32WBA_EXTI : BasicDoubleWordPeripheral, IKnownSize, ILocalGPIOReceiver, INumberedGPIOOutput 18 { STM32WBA_EXTI(IMachine machine, int numberOfOutputLines)19 public STM32WBA_EXTI(IMachine machine, int numberOfOutputLines): base(machine) 20 { 21 this.numberOfLines = numberOfOutputLines; 22 core = new STM32_EXTICore(this, lineConfigurableMask: 0x1FFFF, separateConfigs: true); 23 var innerConnections = new Dictionary<int, IGPIO>(); 24 for(var i = 0; i < numberOfOutputLines; ++i) 25 { 26 innerConnections[i] = new GPIO(); 27 } 28 Connections = new ReadOnlyDictionary<int, IGPIO>(innerConnections); 29 internalReceiversCache = new Dictionary<int, InternalReceiver>(); 30 DefineRegisters(); 31 } 32 GetLocalReceiver(int index)33 public IGPIOReceiver GetLocalReceiver(int index) 34 { 35 if(!internalReceiversCache.TryGetValue(index, out var receiver)) 36 { 37 receiver = new InternalReceiver(this, index); 38 internalReceiversCache.Add(index, receiver); 39 } 40 return receiver; 41 } 42 Reset()43 public override void Reset() 44 { 45 base.Reset(); 46 foreach(var connection in Connections.Values) 47 { 48 connection.Unset(); 49 } 50 foreach(var receiver in internalReceiversCache.Values) 51 { 52 for(int pin = 0; pin < GpioPins; ++pin) 53 { 54 receiver.UpdateGPIO(pin); 55 } 56 } 57 } 58 59 public long Size => 0x1000; 60 61 public IReadOnlyDictionary<int, IGPIO> Connections { get; } 62 DefineRegisters()63 private void DefineRegisters() 64 { 65 RegistersCollection.DefineRegister((long)Registers.RaisingTriggerSelection) 66 .WithValueField(0, 17, out core.RisingEdgeMask, name: "RT") 67 .WithReservedBits(17, 15); 68 69 RegistersCollection.DefineRegister((long)Registers.FallingTriggerSelection) 70 .WithValueField(0, 17, out core.FallingEdgeMask, name: "FT") 71 .WithReservedBits(17, 15); 72 73 RegistersCollection.DefineRegister((long)Registers.SoftwareInterruptEvent) 74 .WithValueField(0, 32, name: "SWIER", changeCallback: (_, value) => 75 { 76 BitHelper.ForeachActiveBit(value & core.InterruptMask.Value, bit => 77 { 78 Connections[bit].Set(); 79 }); 80 }); 81 82 RegistersCollection.DefineRegister((long)Registers.RaisingTriggerPending) 83 .WithValueField(0, numberOfLines, out core.PendingRaisingInterrupts, 84 writeCallback: (_, val) => BitHelper.ForeachActiveBit(val, x => Connections[x].Unset()), name: "RPIF"); 85 86 RegistersCollection.DefineRegister((long)Registers.FallingTriggerPending) 87 .WithValueField(0, numberOfLines, out core.PendingFallingInterrupts, 88 writeCallback: (_, val) => BitHelper.ForeachActiveBit(val, x => Connections[x].Unset()), name: "FPIF"); 89 90 RegistersCollection.DefineRegister((long)Registers.SecurityConfiguration); 91 92 RegistersCollection.DefineRegister((long)Registers.PrivilegeConfiguration); 93 94 for(var registerIndex = 0; registerIndex < InterruptSelectionRegistersCount; registerIndex++) 95 { 96 var reg = new DoubleWordRegister(this, 0); 97 for(var fieldNumber = 0; fieldNumber < NumberOfPortsPerInterruptSelectionRegister; ++fieldNumber) 98 { 99 var pinNumber = registerIndex * 4 + fieldNumber; 100 extiMappings[pinNumber] = reg.DefineValueField(8 * fieldNumber, 8, name: $"EXTI{pinNumber}", 101 changeCallback: (_, portNumber) => 102 { 103 Connections[pinNumber].Unset(); 104 ((InternalReceiver)GetLocalReceiver((int)portNumber)).UpdateGPIO(pinNumber); 105 } 106 ); 107 } 108 RegistersCollection.AddRegister((long)Registers.ExternalInterruptSelection1 + 4 * registerIndex, reg); 109 } 110 111 RegistersCollection.DefineRegister((long)Registers.Lock); 112 113 RegistersCollection.DefineRegister((long)Registers.WakeUpInterruptMask); 114 115 RegistersCollection.DefineRegister((long)Registers.WakeUpEventMask); 116 } 117 118 private readonly STM32_EXTICore core; 119 private readonly int numberOfLines; 120 private readonly Dictionary<int, InternalReceiver> internalReceiversCache; 121 private readonly IValueRegisterField[] extiMappings = new IValueRegisterField[GpioPins]; 122 123 private const uint InterruptSelectionRegistersCount = 4; 124 private const int GpioPins = 16; 125 private const int NumberOfPortsPerInterruptSelectionRegister = GpioPins / (int)InterruptSelectionRegistersCount; 126 127 private class InternalReceiver : IGPIOReceiver 128 { InternalReceiver(STM32WBA_EXTI parent, int portNumber)129 public InternalReceiver(STM32WBA_EXTI parent, int portNumber) 130 { 131 this.parent = parent; 132 this.portNumber = portNumber; 133 this.state = new bool[GpioPins]; 134 } 135 OnGPIO(int pinNumber, bool value)136 public void OnGPIO(int pinNumber, bool value) 137 { 138 if(pinNumber >= GpioPins) 139 { 140 parent.Log(LogLevel.Error, "GPIO port {0}, pin {1}, is not supported. Up to {2} pins are supported", portNumber, pinNumber, GpioPins); 141 return; 142 } 143 parent.Log(LogLevel.Noisy, "GPIO port {0}, pin {1}, raised IRQ: {2}", portNumber, pinNumber, value); 144 state[pinNumber] = value; 145 146 UpdateGPIO(pinNumber); 147 } 148 UpdateGPIO(int pinNumber)149 public void UpdateGPIO(int pinNumber) 150 { 151 if((int)parent.extiMappings[pinNumber].Value == portNumber) 152 { 153 var value = state[pinNumber]; 154 if(parent.core.CanSetInterruptValue((byte)pinNumber, value, out var _)) 155 { 156 parent.core.UpdatePendingValue((byte)pinNumber, true); 157 parent.Connections[pinNumber].Set(true); 158 } 159 } 160 } 161 Reset()162 public void Reset() 163 { 164 // IRQs are cleared on Parent reset 165 // Don't clear `state` array here - as it represents the state of input signals, and is not a property of this peripheral 166 // The state can only be cleared when the input signal is reset - but it's not controlled by us, but by the peripheral connected to OnGPIO (IRQ/GPIO line) 167 // and since peripherals with connected GPIOs will naturally unset them in their Reset, the state array won't contain stale data 168 } 169 170 // The state is recorded, so it's possible to update the GPIO state when changing source peripheral 171 // since this peripheral is effectively a mux - when changing input source, it's needed to get the other source's value 172 private readonly bool[] state; 173 private readonly STM32WBA_EXTI parent; 174 private readonly int portNumber; 175 } 176 177 178 private enum Registers 179 { 180 RaisingTriggerSelection = 0x0, // EXTI_RTSR1 181 FallingTriggerSelection = 0x4, // EXTI_FTSR1 182 SoftwareInterruptEvent = 0x8, // EXTI_SWIER1 183 RaisingTriggerPending = 0xc, // EXTI_RPR1 184 FallingTriggerPending = 0x10, // EXTI_FPR1 185 SecurityConfiguration = 0x14, // EXTI_SECCFGR1 186 PrivilegeConfiguration = 0x18, // EXTI_PRIVCFGR1 187 ExternalInterruptSelection1 = 0x60, // EXTI_EXTICR1 188 ExternalInterruptSelection2 = 0x64, // EXTI_EXTICR2 189 ExternalInterruptSelection3 = 0x68, // EXTI_EXTICR3 190 ExternalInterruptSelection4 = 0x6c, // EXTI_EXTICR4 191 Lock = 0x70, // EXTI_LOCKR 192 WakeUpInterruptMask = 0x80, // EXTI_IMR1 193 WakeUpEventMask = 0x84, // EXTI_EMR1 194 } 195 } 196 } 197