1 //
2 // Copyright (c) 2010-2021 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;
8 using System.Linq;
9 using Antmicro.Renode.Core;
10 using Antmicro.Renode.Peripherals.Bus;
11 using Antmicro.Renode.Core.Structure.Registers;
12 using Antmicro.Renode.Logging;
13 
14 namespace Antmicro.Renode.Peripherals.GPIOPort
15 {
16     public class NRF52840_GPIO : BaseGPIOPort, IProvidesRegisterCollection<DoubleWordRegisterCollection>, IDoubleWordPeripheral, IKnownSize
17     {
NRF52840_GPIO(IMachine machine)18         public NRF52840_GPIO(IMachine machine) : base(machine, NumberOfPins)
19         {
20             Pins = new Pin[NumberOfPins];
21             for(var i = 0; i < Pins.Length; i++)
22             {
23                 Pins[i] = new Pin(this, i);
24             }
25 
26             RegistersCollection = new DoubleWordRegisterCollection(this);
27             DefineRegisters();
28         }
29 
Reset()30         public override void Reset()
31         {
32             base.Reset();
33 
34             RegistersCollection.Reset();
35 
36             foreach(var pin in Pins)
37             {
38                 pin.Reset();
39             }
40 
41             detectState = false;
42         }
43 
ReadDoubleWord(long offset)44         public uint ReadDoubleWord(long offset)
45         {
46             return RegistersCollection.Read(offset);
47         }
48 
WriteDoubleWord(long offset, uint value)49         public void WriteDoubleWord(long offset, uint value)
50         {
51             RegistersCollection.Write(offset, value);
52         }
53 
OnGPIO(int number, bool value)54         public override void OnGPIO(int number, bool value)
55         {
56             if(CheckPinNumber(number))
57             {
58                 Pins[number].InputValue = value;
59             }
60         }
61 
62         public DoubleWordRegisterCollection RegistersCollection { get; }
63         public Pin[] Pins { get; }
64 
65         public long Size => 0x300;
66 
67         public event Action<Pin, bool> PinChanged;
68         public event Action Detect;
69 
DefineRegisters()70         private void DefineRegisters()
71         {
72             Registers.Out.Define(this)
73                 .WithFlags(0, NumberOfPins,
74                     valueProviderCallback: (id, _) => Pins[id].OutputValue,
75                     writeCallback: (id, _, val) => Pins[id].OutputValue = val)
76             ;
77 
78             Registers.OutSet.Define(this)
79                 .WithFlags(0, NumberOfPins,
80                     valueProviderCallback: (id, _) => Pins[id].OutputValue,
81                     writeCallback: (id, _, val) =>
82                     {
83                         if(val)
84                         {
85                             Pins[id].OutputValue = true;
86                         }
87                     })
88             ;
89 
90             Registers.OutClear.Define(this)
91                 .WithFlags(0, NumberOfPins,
92                     valueProviderCallback: (id, _) => Pins[id].OutputValue,
93                     writeCallback: (id, _, val) =>
94                     {
95                         if(val)
96                         {
97                             Pins[id].OutputValue = false;
98                         }
99                     })
100             ;
101 
102             Registers.In.Define(this)
103                 .WithFlags(0, NumberOfPins, FieldMode.Read,
104                     valueProviderCallback: (id, _) => Pins[id].InputValue)
105             ;
106 
107             Registers.Direction.Define(this)
108                 .WithFlags(0, NumberOfPins,
109                     valueProviderCallback: (id, _) => Pins[id].Direction == PinDirection.Output,
110                     writeCallback: (id, _, val) => Pins[id].Direction = val ? PinDirection.Output : PinDirection.Input)
111             ;
112 
113             Registers.DirectionSet.Define(this)
114                 .WithFlags(0, NumberOfPins,
115                     valueProviderCallback: (id, _) => Pins[id].Direction == PinDirection.Output,
116                     writeCallback: (id, _, val) => { if(val) Pins[id].Direction = PinDirection.Output; })
117             ;
118 
119             Registers.DirectionClear.Define(this)
120                 .WithFlags(0, NumberOfPins,
121                     valueProviderCallback: (id, _) => Pins[id].Direction == PinDirection.Output,
122                     writeCallback: (id, _, val) => { if(val) Pins[id].Direction = PinDirection.Input; })
123             ;
124 
125             Registers.Latch.Define(this)
126                 .WithTag("LATCH", 0, 32)
127             ;
128 
129             Registers.DetectMode.Define(this)
130                 .WithTaggedFlag("DETECTMODE", 0)
131                 .WithReservedBits(1, 31)
132             ;
133 
134             Registers.PinConfigure.DefineMany(this, NumberOfPins, (register, idx) =>
135             {
136                 register
137                     .WithFlag(0, name: "DIR",
138                         writeCallback: (_, val) =>
139                         {
140                             var newValue = val ? PinDirection.Output : PinDirection.Input;
141                             if(newValue != Pins[idx].Direction)
142                             {
143                                 Pins[idx].Direction = newValue;
144                             }
145                         },
146                         valueProviderCallback: _ => Pins[idx].Direction == PinDirection.Output)
147                     .WithFlag(1, name: "INPUT",
148                         writeCallback: (_, val) => Pins[idx].InputOverride = !val,
149                         valueProviderCallback: _ => !Pins[idx].InputOverride)
150                     .WithEnumField<DoubleWordRegister, PullMode>(2, 2, name: "PULL",
151                         writeCallback: (_, val) =>
152                         {
153                             Pins[idx].PullMode = val;
154                             switch(val)
155                             {
156                                 case PullMode.PullUp:
157                                     Pins[idx].DefaultValue = true;
158                                     break;
159                                 case PullMode.PullDown:
160                                     Pins[idx].DefaultValue = false;
161                                     break;
162                                 default:
163                                     break;
164                             }
165                         },
166                         valueProviderCallback: _ => Pins[idx].PullMode)
167                     .WithReservedBits(4, 4)
168                     .WithEnumField<DoubleWordRegister, DriveMode>(8, 3, name: "DRIVE",
169                         valueProviderCallback: _ => Pins[idx].DriveMode,
170                         writeCallback: (_, val) => Pins[idx].DriveMode = val
171                     )
172                     .WithReservedBits(11, 5)
173                     .WithEnumField<DoubleWordRegister, SenseMode>(16, 2, name: "SENSE",
174                         writeCallback: (_, val) =>
175                         {
176                             if(Pins[idx].SenseMode != val)
177                             {
178                                 Pins[idx].SenseMode = val;
179                                 UpdateDetect();
180                             }
181                         },
182                         valueProviderCallback: _ => Pins[idx].SenseMode)
183                     .WithReservedBits(18, 14)
184                 ;
185             });
186         }
187 
UpdateDetect()188         private void UpdateDetect()
189         {
190             var nextDetectState = Pins.Any(x => x.IsSensing);
191             if(nextDetectState != detectState)
192             {
193                 detectState = nextDetectState;
194                 if(nextDetectState)
195                 {
196                     Detect?.Invoke();
197                 }
198             }
199         }
200 
201         private bool detectState;
202 
203         private const int NumberOfPins = 32;
204 
205         public class Pin
206         {
Pin(NRF52840_GPIO parent, int id)207             public Pin(NRF52840_GPIO parent, int id)
208             {
209                 this.Parent = parent;
210                 this.Id = id;
211             }
212 
Reset()213             public void Reset()
214             {
215                 Parent.Connections[Id].Set(false);
216                 InputOverride = false;
217                 everSet = false;
218                 bufferedInputValue = false;
219                 bufferedOutputValue = false;
220                 direction = PinDirection.Input;
221             }
222 
223             public bool OutputValue
224             {
225                 get => GetCurrentValue();
226 
227                 set
228                 {
229                     if(Direction != PinDirection.Output)
230                     {
231                         bufferedOutputValue = value;
232                         return;
233                     }
234 
235                     Parent.NoisyLog("Setting pin {0} output to {1}", Id, value);
236                     Parent.Connections[Id].Set(value);
237                 }
238             }
239 
240             public bool InputValue
241             {
242                 get => GetCurrentValue();
243 
244                 set
245                 {
246                     everSet = true;
247 
248                     if(Direction != PinDirection.Input)
249                     {
250                         // buffer the value so that it can be used on a next direction change
251                         bufferedInputValue = value;
252                         return;
253                     }
254 
255                     if(value == Parent.State[Id])
256                     {
257                         return;
258                     }
259 
260                     Parent.NoisyLog("Setting pin {0} input to {1}", Id, value);
261                     Parent.State[Id] = value;
262                     Parent.PinChanged(this, value);
263                     Parent.UpdateDetect();
264                 }
265             }
266 
267             // This property is needed as the pull-up and pull-down should be
268             // able to override Pin state not matter Direction it is set to.
269             public bool DefaultValue { get; set; }
270 
271             public PinDirection Direction
272             {
273                 get => direction;
274 
275                 set
276                 {
277                     if(direction == value)
278                     {
279                         return;
280                     }
281 
282                     direction = value;
283                     if(direction == PinDirection.Input)
284                     {
285                         InputValue = bufferedInputValue;
286                     }
287                     else
288                     {
289                         OutputValue = bufferedOutputValue;
290                     }
291                 }
292             }
293 
294             public bool InputOverride { set; get; }
295 
296             public DriveMode DriveMode { set; get; }
297 
298             public SenseMode SenseMode { get; set; }
299 
300             public PullMode PullMode { get; set; }
301 
302             public bool IsSensing
303             {
304                 get
305                 {
306                     if(SenseMode == SenseMode.Disabled)
307                     {
308                         return false;
309                     }
310 
311                     return (SenseMode == SenseMode.High && InputValue) || (SenseMode == SenseMode.Low && !InputValue);
312                 }
313             }
314 
315             public NRF52840_GPIO Parent { get; }
316             public int Id { get; }
317 
318             // returns the pin value depending on the currently set direction
GetCurrentValue()319             private bool GetCurrentValue()
320             {
321                 if(Direction == PinDirection.Output)
322                 {
323                     return Parent.Connections[Id].IsSet;
324                 }
325                 else
326                 {
327                     return everSet ? Parent.State[Id] : DefaultValue;
328                 }
329             }
330 
331             private bool everSet;
332             private bool bufferedInputValue;
333             private bool bufferedOutputValue;
334             private PinDirection direction;
335         }
336 
337         public enum SenseMode
338         {
339             Disabled = 0,
340             High = 2,
341             Low = 3
342         }
343 
344         public enum PullMode
345         {
346             Disabled = 0,
347             PullDown = 1,
348             PullUp = 3,
349         }
350 
351         public enum DriveMode
352         {
353             Standard = 0,
354             HighDriveZero,
355             HighDriveOne,
356             HighDrive,
357             OpenZeroStandardOne,
358             OpenZeroHighDriveOne,
359             StandardZeroOpenOne,
360             HighDriveZeroOpenOne
361         }
362 
363         public enum PinDirection
364         {
365             Input,
366             Output
367         }
368 
369         private enum Registers
370         {
371             Out = 0x4,
372             OutSet = 0x8,
373             OutClear = 0xC,
374             In = 0x10,
375             Direction = 0x14,
376             DirectionSet = 0x18,
377             DirectionClear = 0x1C,
378             Latch = 0x20,
379             DetectMode = 0x24,
380             // this is a group of 32 registers for each pin
381             PinConfigure = 0x200
382         }
383     }
384 }
385