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