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 using System;
8 using System.Linq;
9 using System.Collections.Generic;
10 using Antmicro.Renode.Core;
11 using Antmicro.Renode.Peripherals.Bus;
12 using Antmicro.Renode.Core.Structure.Registers;
13 using Antmicro.Renode.Logging;
14 
15 namespace Antmicro.Renode.Peripherals.GPIOPort
16 {
17     public class NRF52840_GPIOTasksEvents : BasicDoubleWordPeripheral, IKnownSize
18     {
NRF52840_GPIOTasksEvents(IMachine machine, NRF52840_GPIO port0 = null, NRF52840_GPIO port1 = null)19         public NRF52840_GPIOTasksEvents(IMachine machine, NRF52840_GPIO port0 = null, NRF52840_GPIO port1 = null) : base(machine)
20         {
21             IRQ = new GPIO();
22             DefineRegisters();
23 
24             ports = new [] { port0, port1 };
25             if(port0 != null)
26             {
27                 port0.PinChanged += OnPinChanged;
28                 port0.Detect += OnDetect;
29             }
30             if(port1 != null)
31             {
32                 port1.PinChanged += OnPinChanged;
33                 port1.Detect += OnDetect;
34             }
35 
36             pinToChannelMapping = new Dictionary<NRF52840_GPIO.Pin, Channel>();
37             channels = new Channel[NumberOfChannels];
38             for(var i = 0; i < channels.Length; i++)
39             {
40                 channels[i] = new Channel(i, this);
41             }
42         }
43 
Reset()44         public override void Reset()
45         {
46             base.Reset();
47             foreach(var ch in channels)
48             {
49                 ch.Reset();
50             }
51             pinToChannelMapping.Clear();
52 
53             UpdateInterrupt();
54         }
55 
56         public GPIO IRQ { get; }
57 
58         public long Size => 0x1000;
59 
DefineRegisters()60         private void DefineRegisters()
61         {
62             Registers.TasksOut.DefineMany(this, NumberOfChannels, (register, idx) => {
63                 register
64                     .WithFlag(0, FieldMode.Write, name: "TASKS_OUT",
65                         writeCallback: (_, val) => channels[idx].WritePin(val))
66                     .WithReservedBits(1, 31);
67             });
68 
69             Registers.TasksSet.DefineMany(this, NumberOfChannels, (register, idx) => {
70                 register
71                     .WithFlag(0, FieldMode.Write, name: "TASKS_SET",
72                         writeCallback: (_, val) => { if(val) channels[idx].SetPin(); })
73                     .WithReservedBits(1, 31);
74             });
75 
76             Registers.TasksClear.DefineMany(this, NumberOfChannels, (register, idx) => {
77                 register
78                     .WithFlag(0, FieldMode.Write, name: "TASKS_CLR",
79                         writeCallback: (_, val) => { if(val) channels[idx].ClearPin(); })
80                     .WithReservedBits(1, 31);
81             });
82 
83             Registers.EventsIn.DefineMany(this, NumberOfChannels, (register, idx) => {
84                 register
85                     .WithFlag(0, name: "EVENTS_IN",
86                         valueProviderCallback: _ => channels[idx].EventPending,
87                         writeCallback: (_, val) => channels[idx].EventPending = val)
88                     .WithReservedBits(1, 31)
89                     .WithWriteCallback((_, __) => UpdateInterrupt());
90             });
91 
92             Registers.EventsPort.Define(this)
93                 .WithFlag(0, out portInterruptPending)
94                 .WithReservedBits(1, 31)
95                 .WithWriteCallback((_, __) => UpdateInterrupt())
96             ;
97 
98             Registers.EnableInterrupt.Define(this)
99                 .WithFlags(0, 8, name: "EVENT_IN",
100                         valueProviderCallback: (idx, _) => channels[idx].EventEnabled,
101                         writeCallback: (idx, _, val) => { if(val) channels[idx].EventEnabled = true; })
102                 .WithReservedBits(8, 23)
103                 .WithFlag(31, out portInterruptEnabled, FieldMode.Read | FieldMode.Set, name: "PORT")
104                 .WithWriteCallback((_, __) => UpdateInterrupt())
105             ;
106 
107             Registers.DisableInterrupt.Define(this)
108                 .WithFlags(0, 8, name: "EVENT_IN",
109                         valueProviderCallback: (idx, _) => channels[idx].EventEnabled,
110                         writeCallback: (idx, _, val) => { if(val) channels[idx].EventEnabled = false; })
111                 .WithReservedBits(8, 23)
112                 .WithFlag(31, FieldMode.Read | FieldMode.WriteOneToClear, name: "PORT",
113                     valueProviderCallback: _ => portInterruptEnabled.Value,
114                     writeCallback: (_, val) => { if(val) portInterruptEnabled.Value = false; })
115                 .WithWriteCallback((_, __) => UpdateInterrupt())
116             ;
117 
118             Registers.Configuration.DefineMany(this, NumberOfChannels, (register, idx) => {
119                 register
120                     .WithEnumField<DoubleWordRegister,Mode>(0, 2, name: "MODE",
121                         valueProviderCallback: _ => channels[idx].Mode,
122                         writeCallback: (_, val) => channels[idx].Mode = val)
123                     .WithReservedBits(2, 6)
124                     .WithValueField(8, 5, name: "PSEL",
125                         valueProviderCallback: _ => channels[idx].SelectedPin,
126                         writeCallback: (_, val) => channels[idx].SelectedPin = (uint)val)
127                     .WithValueField(13, 1, name: "PORT",
128                         valueProviderCallback: _ => channels[idx].SelectedPort,
129                         writeCallback: (_, val) => channels[idx].SelectedPort = (uint)val)
130                     .WithReservedBits(14, 2)
131                     .WithEnumField<DoubleWordRegister, Polarity>(16, 2, name: "POLARITY",
132                         valueProviderCallback: _ => channels[idx].Polarity,
133                         writeCallback: (_, val) => channels[idx].Polarity = val)
134                     .WithReservedBits(18, 2)
135                     .WithFlag(20, name: "OUTINIT",
136                         valueProviderCallback: _ => channels[idx].CurrentState,
137                         writeCallback: (_, val) => channels[idx].CurrentState = val)
138                     .WithReservedBits(21, 11)
139                     .WithWriteCallback((_, __) =>
140                     {
141                         pinToChannelMapping.Clear();
142                         UpdateInterrupt();
143                     });
144             });
145         }
146 
TryGetChannel(NRF52840_GPIO.Pin pin, out Channel channel)147         private bool TryGetChannel(NRF52840_GPIO.Pin pin, out Channel channel)
148         {
149             if(pinToChannelMapping.TryGetValue(pin, out channel))
150             {
151                 return true;
152             }
153 
154             foreach(var ch in channels)
155             {
156                 if(pin.Id == ch.SelectedPin && pin.Parent == ports[ch.SelectedPort])
157                 {
158                     pinToChannelMapping[pin] = ch;
159                     channel = ch;
160                     return true;
161                 }
162             }
163 
164             channel = null;
165             return false;
166         }
167 
OnPinChanged(NRF52840_GPIO.Pin pin, bool value)168         private void OnPinChanged(NRF52840_GPIO.Pin pin, bool value)
169         {
170             if((pin.Direction != NRF52840_GPIO.PinDirection.Input && !pin.InputOverride)
171                || !TryGetChannel(pin, out var channel))
172             {
173                 return;
174             }
175 
176             if(channel.Mode != Mode.Event)
177             {
178                 channel.CurrentState = value;
179                 return;
180             }
181 
182             switch(channel.Polarity)
183             {
184                 case Polarity.None:
185                     channel.EventPending = false;
186                     break;
187 
188                 case Polarity.LoToHi:
189                     channel.EventPending = !channel.CurrentState && value;
190                     break;
191 
192                 case Polarity.HiToLo:
193                     channel.EventPending = channel.CurrentState && !value;
194                     break;
195 
196                 case Polarity.Toggle:
197                     channel.EventPending = channel.CurrentState != value;
198                     break;
199             }
200 
201             channel.CurrentState = value;
202             UpdateInterrupt();
203         }
204 
OnDetect()205         private void OnDetect()
206         {
207             portInterruptPending.Value = true;
208             UpdateInterrupt();
209         }
210 
UpdateInterrupt()211         private void UpdateInterrupt()
212         {
213             var flag = false;
214             foreach(var ch in channels)
215             {
216                 portInterruptPending.Value |= ch.EventPending;
217                 flag |= ch.EventPending && ch.EventEnabled;
218             }
219 
220             flag |= portInterruptPending.Value && portInterruptEnabled.Value;
221 
222             this.NoisyLog("Setting IRQ to {0}", flag);
223             IRQ.Set(flag);
224         }
225 
226         private readonly uint NumberOfChannels = 8;
227 
228         private IFlagRegisterField portInterruptPending;
229         private IFlagRegisterField portInterruptEnabled;
230 
231         private readonly Dictionary<NRF52840_GPIO.Pin, Channel> pinToChannelMapping;
232         private readonly Channel[] channels;
233         private readonly NRF52840_GPIO[] ports;
234 
235         private class Channel
236         {
Channel(int id, NRF52840_GPIOTasksEvents parent)237             public Channel(int id, NRF52840_GPIOTasksEvents parent)
238             {
239                 this.id = id;
240                 this.parent = parent;
241             }
242 
WritePin(bool value)243             public void WritePin(bool value)
244             {
245                 switch(Polarity)
246                 {
247                     case Polarity.None:
248                         break;
249 
250                     case Polarity.LoToHi:
251                         WritePinInner(true);
252                         break;
253 
254                     case Polarity.HiToLo:
255                         WritePinInner(false);
256                         break;
257 
258                     case Polarity.Toggle:
259                         WritePinInner(toggle: true);
260                         break;
261                 }
262             }
263 
SetPin()264             public void SetPin()
265             {
266                 WritePinInner(true);
267             }
268 
ClearPin()269             public void ClearPin()
270             {
271                 WritePinInner(false);
272             }
273 
Reset()274             public void Reset()
275             {
276                 Mode = Mode.Disabled;
277                 SelectedPin = 0;
278                 SelectedPort = 0;
279                 Polarity = Polarity.None;
280                 CurrentState = false;
281 
282                 EventEnabled = false;
283                 EventPending = false;
284             }
285 
TryGetPin(out NRF52840_GPIO.Pin pin)286             private bool TryGetPin(out NRF52840_GPIO.Pin pin)
287             {
288                 var port = parent.ports[SelectedPort];
289                 if(port == null)
290                 {
291                     parent.Log(LogLevel.Warning, "Trying to access a not connected port #{0}", SelectedPort);
292                     pin = null;
293                     return false;
294                 }
295 
296                 if(SelectedPin >= port.Pins.Length)
297                 {
298                     parent.Log(LogLevel.Warning, "Trying to access a not existing pin #{0} in port #{1}", SelectedPin, SelectedPort);
299                     pin = null;
300                     return false;
301                 }
302 
303                 pin = port.Pins[SelectedPin];
304                 return true;
305             }
306 
WritePinInner(bool value = false, bool toggle = false)307             private void WritePinInner(bool value = false, bool toggle = false)
308             {
309                 if(Mode != Mode.Task)
310                 {
311                     parent.Log(LogLevel.Warning, "Setting channel #{0} not configured as TASK", id);
312                     return;
313                 }
314 
315                 if(!TryGetPin(out var pin))
316                 {
317                     return;
318                 }
319 
320                 pin.Direction = NRF52840_GPIO.PinDirection.Output;
321                 pin.OutputValue = toggle
322                     ? !pin.OutputValue
323                     : value;
324             }
325 
326             public Mode Mode { get; set; }
327 
328             public uint SelectedPin { get; set; }
329             public uint SelectedPort { get; set; }
330 
331             public Polarity Polarity { get; set; }
332 
333             public bool CurrentState { get; set; }
334 
335             public bool EventPending { get; set; }
336             public bool EventEnabled { get; set; }
337 
338             private readonly int id;
339             private readonly NRF52840_GPIOTasksEvents parent;
340         }
341 
342         private enum Mode
343         {
344             Disabled = 0,
345             Event = 1,
346             Task = 3
347         }
348 
349         private enum Polarity
350         {
351             None = 0,
352             LoToHi = 1,
353             HiToLo = 2,
354             Toggle = 3
355         }
356 
357         private enum Registers
358         {
359             // this is a group of 8 registers
360             TasksOut = 0x0,
361             // this is a group of 8 registers
362             TasksSet = 0x30,
363             // this is a group of 8 registers
364             TasksClear = 0x60,
365             // this is a group of 8 registers
366             EventsIn = 0x100,
367 
368             EventsPort = 0x17C,
369 
370             EnableInterrupt = 0x304,
371             DisableInterrupt = 0x308,
372 
373             // this is a group of 8 registers
374             Configuration = 0x510
375         }
376     }
377 }
378