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 8 using System; 9 using System.Collections.Generic; 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Logging; 12 using Antmicro.Renode.Utilities; 13 using Antmicro.Renode.Exceptions; 14 using Antmicro.Renode.Peripherals.Bus; 15 using Antmicro.Renode.Core.Structure.Registers; 16 17 namespace Antmicro.Renode.Peripherals.GPIOPort 18 { 19 [AllowedTranslations(AllowedTranslation.WordToDoubleWord)] 20 public class STM32_GPIOPort : BaseGPIOPort, IDoubleWordPeripheral, ILocalGPIOReceiver 21 { STM32_GPIOPort(IMachine machine, uint modeResetValue = 0, uint outputSpeedResetValue = 0, uint pullUpPullDownResetValue = 0, uint numberOfAFs = 16)22 public STM32_GPIOPort(IMachine machine, uint modeResetValue = 0, uint outputSpeedResetValue = 0, uint pullUpPullDownResetValue = 0, 23 uint numberOfAFs = 16) : base(machine, NumberOfPins) 24 { 25 if(numberOfAFs < 1 || numberOfAFs > 16) 26 { 27 throw new ConstructionException("Number of alternate functions can't be lower than 1 or higher than 16"); 28 } 29 30 mode = new Mode[NumberOfPins]; 31 outputSpeed = new OutputSpeed[NumberOfPins]; 32 pullUpPullDown = new PullUpPullDown[NumberOfPins]; 33 34 this.modeResetValue = modeResetValue; 35 this.outputSpeedResetValue = outputSpeedResetValue; 36 this.pullUpPullDownResetValue = pullUpPullDownResetValue; 37 this.numberOfAFs = numberOfAFs; 38 39 alternateFunctionOutputs = new GPIOAlternateFunction[NumberOfPins]; 40 for(var i = 0; i < NumberOfPins; i++) 41 { 42 alternateFunctionOutputs[i] = new GPIOAlternateFunction(this, i); 43 } 44 45 registers = CreateRegisters(); 46 Reset(); 47 } 48 Reset()49 public override void Reset() 50 { 51 base.Reset(); 52 registers.Reset(); 53 54 for(var i = 0; i < NumberOfPins; i++) 55 { 56 // Reset AF outputs before resetting pin modes as changing pin mode can affect whether AF is connected or not. 57 alternateFunctionOutputs[i].Reset(); 58 ChangeMode(i, (Mode)BitHelper.GetValue(modeResetValue, 2 * i, 2)); 59 outputSpeed[i] = (OutputSpeed)BitHelper.GetValue(outputSpeedResetValue, 2 * i, 2); 60 pullUpPullDown[i] = (PullUpPullDown)BitHelper.GetValue(pullUpPullDownResetValue, 2 * i, 2); 61 } 62 } 63 ReadDoubleWord(long offset)64 public uint ReadDoubleWord(long offset) 65 { 66 return registers.Read(offset); 67 } 68 WriteDoubleWord(long offset, uint value)69 public void WriteDoubleWord(long offset, uint value) 70 { 71 registers.Write(offset, value); 72 } 73 OnGPIO(int number, bool value)74 public override void OnGPIO(int number, bool value) 75 { 76 base.OnGPIO(number, value); 77 Connections[number].Set(value); 78 } 79 GetLocalReceiver(int pin)80 public IGPIOReceiver GetLocalReceiver(int pin) 81 { 82 if(pin < 0 || pin >= NumberOfPins) 83 { 84 throw new RecoverableException($"This peripheral supports GPIO inputs from 0 to {NumberOfPins - 1}, but {pin} was called."); 85 } 86 87 return alternateFunctionOutputs[pin]; 88 } 89 WritePin(int number, bool value)90 private void WritePin(int number, bool value) 91 { 92 State[number] = value; 93 Connections[number].Set(value); 94 } 95 WriteState(ushort value)96 private void WriteState(ushort value) 97 { 98 for(var i = 0; i < NumberOfPins; i++) 99 { 100 var state = ((value & 1u) == 1); 101 WritePin(i, state); 102 103 value >>= 1; 104 } 105 } 106 ChangeMode(int number, Mode newMode)107 private void ChangeMode(int number, Mode newMode) 108 { 109 mode[number] = newMode; 110 alternateFunctionOutputs[number].IsConnected = newMode == Mode.AlternateFunction; 111 } 112 CreateRegisters()113 private DoubleWordRegisterCollection CreateRegisters() 114 { 115 var registersMap = new Dictionary<long, DoubleWordRegister> 116 { 117 {(long)Registers.Mode, new DoubleWordRegister(this) 118 .WithEnumFields<DoubleWordRegister, Mode>(0, 2, NumberOfPins, name: "MODER", 119 valueProviderCallback: (idx, _) => mode[idx], 120 writeCallback: (idx, _, val) => ChangeMode(idx, val)) 121 }, 122 {(long)Registers.OutputType, new DoubleWordRegister(this) 123 .WithTaggedFlag("OT0", 0) 124 .WithTaggedFlag("OT1", 1) 125 .WithTaggedFlag("OT2", 2) 126 .WithTaggedFlag("OT3", 3) 127 .WithTaggedFlag("OT4", 4) 128 .WithTaggedFlag("OT5", 5) 129 .WithTaggedFlag("OT6", 6) 130 .WithTaggedFlag("OT7", 7) 131 .WithTaggedFlag("OT8", 8) 132 .WithTaggedFlag("OT9", 9) 133 .WithTaggedFlag("OT10", 10) 134 .WithTaggedFlag("OT11", 11) 135 .WithTaggedFlag("OT12", 12) 136 .WithTaggedFlag("OT13", 13) 137 .WithTaggedFlag("OT14", 14) 138 .WithTaggedFlag("OT15", 15) 139 .WithReservedBits(16, 16) 140 }, 141 {(long)Registers.OutputSpeed, new DoubleWordRegister(this) 142 .WithEnumFields<DoubleWordRegister, OutputSpeed>(0, 2, NumberOfPins, name: "OSPEEDR", 143 valueProviderCallback: (idx, _) => outputSpeed[idx], 144 writeCallback: (idx, _, val) => { outputSpeed[idx] = val; }) 145 }, 146 {(long)Registers.PullUpPullDown, new DoubleWordRegister(this) 147 .WithEnumFields<DoubleWordRegister, PullUpPullDown>(0, 2, NumberOfPins, name: "PUPDR0", 148 valueProviderCallback: (idx, _) => pullUpPullDown[idx], 149 writeCallback: (idx, _, val) => { pullUpPullDown[idx] = val; }) 150 }, 151 {(long)Registers.InputData, new DoubleWordRegister(this) 152 .WithValueField(0, 16, FieldMode.Read, valueProviderCallback: _ => BitHelper.GetValueFromBitsArray(State), name: "IDR") 153 .WithReservedBits(16, 16) 154 }, 155 {(long)Registers.OutputData, new DoubleWordRegister(this) 156 .WithValueField(0, 16, writeCallback: (_, val) => WriteState((ushort)val), valueProviderCallback: _ => BitHelper.GetValueFromBitsArray(State), name: "ODR") 157 .WithReservedBits(16, 16) 158 }, 159 {(long)Registers.BitSet, new DoubleWordRegister(this) 160 .WithValueField(0, 16, FieldMode.Write, 161 writeCallback: (_, val) => { if(val != 0) WriteState((ushort)(BitHelper.GetValueFromBitsArray(State) | val)); }, 162 name: "GPIOx_BS") 163 .WithValueField(16, 16, FieldMode.Write, 164 writeCallback: (_, val) => { if(val != 0) WriteState((ushort)(BitHelper.GetValueFromBitsArray(State) & ~val)); }, 165 name: "GPIOx_BR") 166 }, 167 { (long)Registers.ConfigurationLock, new DoubleWordRegister(this) 168 .WithTaggedFlag("LCK0", 0) 169 .WithTaggedFlag("LCK1", 1) 170 .WithTaggedFlag("LCK2", 2) 171 .WithTaggedFlag("LCK3", 3) 172 .WithTaggedFlag("LCK4", 4) 173 .WithTaggedFlag("LCK5", 5) 174 .WithTaggedFlag("LCK6", 6) 175 .WithTaggedFlag("LCK7", 7) 176 .WithTaggedFlag("LCK8", 8) 177 .WithTaggedFlag("LCK9", 9) 178 .WithTaggedFlag("LCK10", 10) 179 .WithTaggedFlag("LCK11", 11) 180 .WithTaggedFlag("LCK12", 12) 181 .WithTaggedFlag("LCK13", 13) 182 .WithTaggedFlag("LCK14", 14) 183 .WithTaggedFlag("LCK15", 15) 184 .WithTaggedFlag("LCKK", 16) 185 .WithReservedBits(17, 15) 186 }, 187 {(long)Registers.AlternateFunctionLow, new DoubleWordRegister(this) 188 .WithValueFields(0, 4, 8, name: "AFSEL_LO", 189 writeCallback: (i, _, val) => alternateFunctionOutputs[i].ActiveFunction = val, 190 valueProviderCallback: (i, _) => alternateFunctionOutputs[i].ActiveFunction 191 ) 192 }, 193 {(long)Registers.AlternateFunctionHigh, new DoubleWordRegister(this) 194 .WithValueFields(0, 4, 8, name: "AFSEL_HI", 195 writeCallback: (i, _, val) => alternateFunctionOutputs[i + 8].ActiveFunction = val, 196 valueProviderCallback: (i, _) => alternateFunctionOutputs[i + 8].ActiveFunction 197 ) 198 }, 199 {(long)Registers.BitReset, new DoubleWordRegister(this) 200 .WithValueField(0, 16, FieldMode.Write, 201 writeCallback: (_, val) => { if(val != 0) WriteState((ushort)(BitHelper.GetValueFromBitsArray(State) & ~val)); }, 202 name: "GPIOx_BRR") 203 .WithReservedBits(16, 16) 204 }, 205 }; 206 return new DoubleWordRegisterCollection(this, registersMap); 207 } 208 209 private readonly Mode[] mode; 210 private readonly OutputSpeed[] outputSpeed; 211 private readonly PullUpPullDown[] pullUpPullDown; 212 213 private readonly uint modeResetValue; 214 private readonly uint outputSpeedResetValue; 215 private readonly uint pullUpPullDownResetValue; 216 217 private readonly DoubleWordRegisterCollection registers; 218 219 private readonly uint numberOfAFs; 220 // NOTE: This array holds connections from AFs to specific GPIO pins. 221 // From the PoV of this peripheral they're inputs, 222 // however they represent the output pins of this peripheral hence the name. 223 private readonly GPIOAlternateFunction[] alternateFunctionOutputs; 224 // TODO: AF inputs 225 226 private const int NumberOfPins = 16; 227 228 private class GPIOAlternateFunction : IGPIOReceiver 229 { GPIOAlternateFunction(STM32_GPIOPort port, int pin)230 public GPIOAlternateFunction(STM32_GPIOPort port, int pin) 231 { 232 this.port = port; 233 this.pin = pin; 234 235 Reset(); 236 } 237 Reset()238 public void Reset() 239 { 240 IsConnected = false; 241 ActiveFunction = 0; 242 } 243 OnGPIO(int number, bool value)244 public void OnGPIO(int number, bool value) 245 { 246 if(!CheckAFNumber(number) || !IsConnected || number != activeFunction) 247 { 248 // Don't emit any log as it is valid to receive signals from AFs when they are not active. 249 // All alternate function sources are always connected and always sending signals. 250 // The GPIO configuration then decides whether those are connected 251 // to GPIO input/output or are simply ingored. 252 return; 253 } 254 255 port.WritePin(pin, value); 256 } 257 258 public bool IsConnected { get; set; } 259 public ulong ActiveFunction 260 { 261 get => (ulong)activeFunction; 262 set 263 { 264 var val = (int)value; 265 266 if(CheckAFNumber(val)) 267 { 268 activeFunction = val; 269 } 270 } 271 } 272 CheckAFNumber(int number)273 private bool CheckAFNumber(int number) 274 { 275 if(number < 0 || number >= port.numberOfAFs) 276 { 277 this.Log(LogLevel.Error, "Alternate function number must be between 0 and {0}, but {1} was given instead.", port.numberOfAFs - 1, number); 278 return false; 279 } 280 return true; 281 } 282 283 private readonly STM32_GPIOPort port; 284 private readonly int pin; 285 private int activeFunction; 286 } 287 288 private enum Mode 289 { 290 Input = 0x0, 291 Output = 0x1, 292 AlternateFunction = 0x2, 293 AnalogMode = 0x3, 294 } 295 296 private enum OutputSpeed 297 { 298 // The reference manual defines the 'Low' setting with 2 possible values 299 Low1 = 0b00, 300 Low2 = 0b10, 301 Medium = 0b01, 302 High = 0b11, 303 } 304 305 private enum PullUpPullDown 306 { 307 No = 0b00, 308 Up = 0b01, 309 Down = 0b10, 310 Reserved = 0b11, 311 } 312 313 // Source: Chapter 7.4 in RM0090 Cortex M4 Reference Manual (Doc ID 018909 Rev 4) 314 // for STM32F40xxx, STM32F41xxx, STM32F42xxx, STM32F43xxx advanced ARM-based 32-bit MCUs 315 private enum Registers 316 { 317 Mode = 0x00, //GPIOx_MODE Mode register 318 OutputType = 0x04, //GPIOx_OTYPER Output type register 319 OutputSpeed = 0x08, //GPIOx_OSPEEDR Output speed register 320 PullUpPullDown = 0x0C, //GPIOx_PUPDR Pull-up/pull-down register 321 InputData = 0x10, //GPIOx_IDR Input data register 322 OutputData = 0x14, //GPIOx_ODR Output data register 323 BitSet = 0x18, //GPIOx_BSRR Bit set/reset register 324 ConfigurationLock = 0x1C, //GPIOx_LCKR Configuration lock register 325 AlternateFunctionLow = 0x20, //GPIOx_AFRL Alternate function low register 326 AlternateFunctionHigh = 0x24, //GPIOx_AFRH Alternate function high register 327 BitReset = 0x28, //GPIOx_BRR Bit reset register 328 } 329 } 330 } 331