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 8 using System; 9 using System.Collections.Generic; 10 using System.Collections.ObjectModel; 11 using Antmicro.Renode.Core; 12 using Antmicro.Renode.Core.Structure.Registers; 13 using Antmicro.Renode.Logging; 14 using Antmicro.Renode.Peripherals.Bus; 15 16 namespace Antmicro.Renode.Peripherals.Miscellaneous 17 { 18 public sealed class STM32_SYSCFG : IDoubleWordPeripheral, INumberedGPIOOutput, IKnownSize, ILocalGPIOReceiver 19 { STM32_SYSCFG()20 public STM32_SYSCFG() 21 { 22 var gpios = new Dictionary<int, IGPIO>(); 23 for(var i = 0; i < GpioPins; ++i) 24 { 25 gpios.Add(i, new GPIO()); 26 } 27 Connections = new ReadOnlyDictionary<int, IGPIO>(gpios); 28 internalReceiversCache = new Dictionary<int, InternalReceiver>(); 29 registers = CreateRegisters(); 30 } 31 32 /* The pattern to connect IRQs in REPL would be: 33 * `[a-b] -> syscfg@index[a-b]`, where: 34 * -> index - index of the mapped peripheral 35 * -> a, b - the range of exposed GPIO pins (lines to be redirected to EXTI) 36 * since this peripheral maps IRQs line-to-line (e.g. line X of input into line X of output) 37 * the thing that's muxed is the peripheral, the line belongs to (the index) 38 */ GetLocalReceiver(int index)39 public IGPIOReceiver GetLocalReceiver(int index) 40 { 41 if(!internalReceiversCache.TryGetValue(index, out var receiver)) 42 { 43 receiver = new InternalReceiver(this, index); 44 internalReceiversCache.Add(index, receiver); 45 } 46 return receiver; 47 } 48 ReadDoubleWord(long offset)49 public uint ReadDoubleWord(long offset) 50 { 51 return registers.Read(offset); 52 } 53 WriteDoubleWord(long offset, uint value)54 public void WriteDoubleWord(long offset, uint value) 55 { 56 registers.Write(offset, value); 57 } 58 Reset()59 public void Reset() 60 { 61 foreach(var connection in Connections.Values) 62 { 63 connection.Unset(); 64 } 65 foreach(var receiver in internalReceiversCache.Values) 66 { 67 for(int pin = 0; pin < GpioPins; ++pin) 68 { 69 receiver.UpdateGPIO(pin); 70 } 71 } 72 registers.Reset(); 73 } 74 75 public IReadOnlyDictionary<int, IGPIO> Connections 76 { 77 get; private set; 78 } 79 80 public long Size 81 { 82 get 83 { 84 return 0x400; 85 } 86 } 87 CreateRegisters()88 private DoubleWordRegisterCollection CreateRegisters() 89 { 90 var map = new Dictionary<long, DoubleWordRegister>(); 91 for(var regNumber = 0; regNumber < 4; ++regNumber) 92 { 93 var reg = new DoubleWordRegister(this, 0); 94 for(var fieldNumber = 0; fieldNumber < 4; ++fieldNumber) 95 { 96 var rn = regNumber; 97 var fn = fieldNumber; 98 var pinNumber = regNumber * 4 + fieldNumber; 99 extiMappings[pinNumber] = reg.DefineValueField(4 * fieldNumber, 4, name: "EXTI" + pinNumber, 100 changeCallback: (_, portNumber) => 101 { 102 Connections[pinNumber].Unset(); 103 ((InternalReceiver)GetLocalReceiver((int)portNumber)).UpdateGPIO(pinNumber); 104 } 105 ); 106 } 107 map.Add((long)Registers.ExternalInterruptConfiguration1 + 4 * regNumber, reg); 108 } 109 return new DoubleWordRegisterCollection(this, map); 110 } 111 112 private readonly DoubleWordRegisterCollection registers; 113 private readonly Dictionary<int, InternalReceiver> internalReceiversCache; 114 115 private readonly IValueRegisterField[] extiMappings = new IValueRegisterField[GpioPins]; 116 117 private const int GpioPins = 16; 118 119 private class InternalReceiver : IGPIOReceiver 120 { InternalReceiver(STM32_SYSCFG parent, int portNumber)121 public InternalReceiver(STM32_SYSCFG parent, int portNumber) 122 { 123 this.parent = parent; 124 this.portNumber = portNumber; 125 this.state = new bool[GpioPins]; 126 } 127 OnGPIO(int pinNumber, bool value)128 public void OnGPIO(int pinNumber, bool value) 129 { 130 if(pinNumber >= GpioPins) 131 { 132 parent.Log(LogLevel.Error, "GPIO port {0}, pin {1}, is not supported. Up to {2} pins are supported", portNumber, pinNumber, GpioPins); 133 return; 134 } 135 parent.Log(LogLevel.Noisy, "GPIO port {0}, pin {1}, raised IRQ: {2}", portNumber, pinNumber, value); 136 state[pinNumber] = value; 137 UpdateGPIO(pinNumber); 138 } 139 UpdateGPIO(int pinNumber)140 public void UpdateGPIO(int pinNumber) 141 { 142 if((int)parent.extiMappings[pinNumber].Value == portNumber) 143 { 144 parent.Connections[pinNumber].Set(state[pinNumber]); 145 } 146 } 147 Reset()148 public void Reset() 149 { 150 // IRQs are cleared on Parent reset 151 // Don't clear `state` array here - as it represents the state of input signals, and is not a property of this peripheral 152 // 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) 153 // and since peripherals with connected GPIOs will naturally unset them in their Reset, the state array won't contain stale data 154 } 155 156 // The state is recorded, so it's possible to update the GPIO state when changing source peripheral 157 // since this peripheral is effectively a mux - when changing input source, it's needed to get the other source's value 158 private readonly bool[] state; 159 private readonly STM32_SYSCFG parent; 160 private readonly int portNumber; 161 } 162 163 private enum Registers 164 { 165 MemoryRemap = 0x0, 166 PeripheralModeConfiguration = 0x4, 167 ExternalInterruptConfiguration1 = 0x8, 168 ExternalInterruptConfiguration2 = 0xC, 169 ExternalInterruptConfiguration3 = 0x10, 170 ExternalInterruptConfiguration4 = 0x14, 171 ConfigurationRegister = 0x18, 172 CompensationCellControl = 0x20, 173 CompensationCellValue = 0x24, 174 CompensationCellCode = 0x28, 175 PowerControl = 0x2C, 176 PackageType = 0x124, 177 User0 = 0x300, 178 User1 = 0x304, 179 User2 = 0x308, 180 User3 = 0x30C, 181 User4 = 0x310, 182 User5 = 0x314, 183 User6 = 0x318, 184 User7 = 0x31C, 185 User8 = 0x320, 186 User9 = 0x324, 187 User10 = 0x328, 188 User11 = 0x32C, 189 User12 = 0x330, 190 User13 = 0x334, 191 User14 = 0x338, 192 User15 = 0x33C, 193 User16 = 0x340, 194 User17 = 0x344, 195 } 196 } 197 } 198