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.Core.Structure.Registers;
12 using Antmicro.Renode.Logging;
13 using Antmicro.Renode.Peripherals.Bus;
14 using Antmicro.Renode.UserInterface;
15 using Antmicro.Renode.Utilities;
16 
17 namespace Antmicro.Renode.Peripherals.GPIOPort
18 {
19     public class NXPGPIOPort : BaseGPIOPort, IBusPeripheral
20     {
NXPGPIOPort(IMachine machine, int numberOfPins)21         public NXPGPIOPort(IMachine machine, int numberOfPins) : base(machine, numberOfPins)
22         {
23             IRQ = new GPIO();
24             interruptManager = new GPIOInterruptManager(IRQ, State);
25             DefineGPIORegisters();
26             DefinePortRegisters();
27         }
28 
29         [ConnectionRegion("gpio")]
ReadDoubleWordFromGPIO(long offset)30         public uint ReadDoubleWordFromGPIO(long offset)
31         {
32             lock(locker)
33             {
34                 return gpioRegisters.Read(offset);
35             }
36         }
37 
38         [ConnectionRegion("gpio")]
WriteDoubleWordToGPIO(long offset, uint value)39         public void WriteDoubleWordToGPIO(long offset, uint value)
40         {
41             lock(locker)
42             {
43                 gpioRegisters.Write(offset, value);
44             }
45         }
46 
47         [ConnectionRegion("port")]
ReadDoubleWordFromPORT(long offset)48         public uint ReadDoubleWordFromPORT(long offset)
49         {
50             lock(locker)
51             {
52                 return portRegisters.Read(offset);
53             }
54         }
55 
56         [ConnectionRegion("port")]
WriteDoubleWordToPORT(long offset, uint value)57         public void WriteDoubleWordToPORT(long offset, uint value)
58         {
59             lock(locker)
60             {
61                 portRegisters.Write(offset, value);
62             }
63         }
64 
OnGPIO(int number, bool value)65         public override void OnGPIO(int number, bool value)
66         {
67             lock(locker)
68             {
69                 if(number >= inputDisabled.Length)
70                 {
71                     this.Log(LogLevel.Error, "Trying to signal GPIO {0:X}, which is out of range (should be lower than {1:X})", number, inputDisabled.Length);
72                     return;
73                 }
74                 if(inputDisabled[number].Value)
75                 {
76                     return;
77                 }
78                 base.OnGPIO(number, value);
79                 interruptManager.RefreshInterrupts();
80             }
81         }
82 
Reset()83         public override void Reset()
84         {
85             portRegisters.Reset();
86             gpioRegisters.Reset();
87             interruptManager.Reset();
88             IRQ.Unset();
89         }
90 
91         [UiAccessible]
PrintCurrentConfiguration()92         public string[,] PrintCurrentConfiguration()
93         {
94             const string notApplicable = "---";
95             const string disabled = "Disabled";
96             var result = new Table();
97             result.AddRow("Pin", "Direction", "State", "Input enabled", "Trigger mode", "Active interrupt");
98             for(var i = 0; i < NumberOfConnections; i++)
99             {
100                 var isInput = interruptManager.PinDirection[i] == GPIOInterruptManager.Direction.Input;
101                 result.AddRow(
102                     i.ToString(),
103                     interruptManager.PinDirection[i].ToString(),
104                     isInput ? State[i].ToString() : Connections[i].IsSet.ToString(),
105                     isInput ? (!inputDisabled[i].Value).ToString() : notApplicable,
106                     isInput
107                         ? (interruptManager.InterruptEnable[i]
108                             ? interruptManager.InterruptType[i].ToString()
109                             : disabled)
110                         : notApplicable,
111                     isInput ? interruptManager.ActiveInterrupts.ElementAt(i).ToString() : notApplicable
112                 );
113             }
114             return result.ToArray();
115         }
116 
117         public GPIO IRQ { get; }
118 
DefinePortRegisters()119         private void DefinePortRegisters()
120         {
121             var registers = new Dictionary<long, DoubleWordRegister>
122             {
123                 {(long)Registers.GlobalPinControlLow, new DoubleWordRegister(this)
124                     .WithValueField(0, 16, out var globalPinWriteEnableLow, FieldMode.Write, name: "GPWE") //order of fields is relevant
125                     .WithValueField(16, 16, FieldMode.Write, writeCallback: (_, value) => GlobalPinControlWrite((uint)value, (uint)globalPinWriteEnableLow.Value, highBits: false, highRegisters: false), name: "GPWE")
126                 },
127                 {(long)Registers.GlobalPinControlHigh, new DoubleWordRegister(this)
128                     .WithValueField(0, 16, out var globalPinWriteEnableHigh, FieldMode.Write, name: "GPWE") //order of fields is relevant
129                     .WithValueField(16, 16, FieldMode.Write, writeCallback: (_, value) => GlobalPinControlWrite((uint)value, (uint)globalPinWriteEnableHigh.Value, highBits: false, highRegisters: true), name: "GPWD")
130                 },
131                 {(long)Registers.GlobalInterruptControlLow, new DoubleWordRegister(this)
132                     .WithValueField(0, 16, out var globalInterruptWriteEnableLow, FieldMode.Write, name: "GIWE") //order of fields is relevant
133                     .WithValueField(16, 16, FieldMode.Write, writeCallback: (_, value) => GlobalPinControlWrite((uint)value, (uint)globalInterruptWriteEnableLow.Value, highBits: true, highRegisters: false), name: "GIWE")
134                 },
135                 {(long)Registers.GlobalInterruptControlHigh, new DoubleWordRegister(this)
136                     .WithValueField(0, 16, out var globalInterruptWriteEnableHigh, FieldMode.Write, name: "GIWE") //order of fields is relevant
137                     .WithValueField(16, 16, FieldMode.Write, writeCallback: (_, value) => GlobalPinControlWrite((uint)value, (uint)globalInterruptWriteEnableHigh.Value, highBits: true, highRegisters: true), name: "GIWD")
138                 },
139                 {(long)Registers.InterruptStatusFlag, new DoubleWordRegister(this)
140                     .WithFlags(0, NumberOfConnections, FieldMode.Read | FieldMode.WriteOneToClear, valueProviderCallback: (i, _) => interruptManager.ActiveInterrupts.ElementAt(i), writeCallback: (i, _, value) =>
141                     {
142                         if(value)
143                         {
144                             interruptManager.ClearInterrupt(i);
145                         }
146                     }, name: "ISF")
147                     .WithWriteCallback((_, __) => UpdateInterrupts())
148                 },
149                 {(long)Registers.DigitalFilterEnable, new DoubleWordRegister(this)
150                     .WithTag("DFE", 0, 32)
151                 },
152                 {(long)Registers.DigitalFilterClock, new DoubleWordRegister(this)
153                     .WithTaggedFlag("CS", 0)
154                     .WithReservedBits(1, 31)
155                 },
156                 {(long)Registers.DigitalFilterWidth, new DoubleWordRegister(this)
157                     .WithTag("FILT", 0, 5)
158                     .WithReservedBits(5, 27)
159                 },
160             };
161 
162             pinControl = new DoubleWordRegister[NumberOfConnections];
163             for(var i = 0; i < NumberOfConnections; ++i)
164             {
165                 var j = i;
166                 pinControl[j] = new DoubleWordRegister(this)
167                     .WithTaggedFlag("PE", 0)
168                     .WithTaggedFlag("PS", 1)
169                     .WithReservedBits(2, 2)
170                     .WithTaggedFlag("PFE", 4)
171                     .WithReservedBits(5, 1)
172                     .WithTaggedFlag("DSE", 6)
173                     .WithReservedBits(7, 1)
174                     .WithTag("MUX", 8, 3)
175                     .WithReservedBits(11, 4)
176                     .WithTaggedFlag("LK", 15)
177                     .WithEnumField<DoubleWordRegister, InterruptConfiguration>(16, 4, writeCallback: (_, value) =>
178                     {
179                         interruptManager.InterruptEnable[j] = value != InterruptConfiguration.Disabled;
180                         interruptManager.InterruptType[j] = CalculateInterruptType(value);
181                         UpdateInterrupts();
182                     }, name: "IRQC")
183                     .WithReservedBits(20, 4)
184                     .WithFlag(24, FieldMode.Read | FieldMode.WriteOneToClear, valueProviderCallback: _ => interruptManager.ActiveInterrupts.ElementAt(j), writeCallback: (_, value) =>
185                     {
186                         if(value)
187                         {
188                             interruptManager.ClearInterrupt(j);
189                             UpdateInterrupts();
190                         }
191                     }, name: "ISF")
192                     .WithReservedBits(25, 7)
193                 ;
194                 registers.Add((long)Registers.PinControlRegisterStart + (0x4 * j), pinControl[j]);
195             }
196 
197             portRegisters = new DoubleWordRegisterCollection(this, registers);
198         }
199 
DefineGPIORegisters()200         private void DefineGPIORegisters()
201         {
202             var registers = new Dictionary<long, DoubleWordRegister>
203             {
204                 {(long)GPIORegisters.DataOutput, new DoubleWordRegister(this)
205                     .WithValueField(0, NumberOfConnections,
206                         valueProviderCallback: _ => GetSetConnectionBits(),
207                         writeCallback: (_, value) => {
208                             for(byte i = 0; i < NumberOfConnections; i++)
209                             {
210                                 if(interruptManager.PinDirection[i] != GPIOInterruptManager.Direction.Output)
211                                 {
212                                     continue;
213                                 }
214                                 Connections[i].Set(BitHelper.IsBitSet(value, i));
215                             }
216                         },
217                         name: "PDOR")
218                     //We could have WithReservedBits depending on NumberOfConnections,
219                     //but we'd need to filter for 32 bits used
220                 },
221                 {(long)GPIORegisters.SetOutput, new DoubleWordRegister(this)
222                     .WithValueField(0, NumberOfConnections, FieldMode.Write,
223                         writeCallback: (_, value) => {
224                             value = FilterForDirection((uint)value, true);
225                             for(byte i = 0; i < NumberOfConnections; i++)
226                             {
227                                 if(BitHelper.IsBitSet(value, i))
228                                 {
229                                     Connections[i].Set();
230                                 }
231                             }
232                         },
233                         name: "PSOR")
234                 },
235                 {(long)GPIORegisters.ClearOutput, new DoubleWordRegister(this)
236                     .WithValueField(0, NumberOfConnections, FieldMode.Write,
237                         writeCallback: (_, value) => {
238                             value = FilterForDirection((uint)value, true);
239                             for(byte i = 0; i < NumberOfConnections; i++)
240                             {
241                                 if(BitHelper.IsBitSet(value, i))
242                                 {
243                                     Connections[i].Unset();
244                                 }
245                             }
246                         },
247                         name: "PCOR")
248                 },
249                 {(long)GPIORegisters.ToggleOutput, new DoubleWordRegister(this)
250                     .WithValueField(0, NumberOfConnections, FieldMode.Write,
251                         writeCallback: (_, value) => {
252                             value = FilterForDirection((uint)value, true);
253                             for(byte i = 0; i < NumberOfConnections; i++)
254                             {
255                                 if(BitHelper.IsBitSet(value, i))
256                                 {
257                                     Connections[i].Toggle();
258                                 }
259                             }
260                         },
261                         name: "PTOR")
262                 },
263                 {(long)GPIORegisters.DataInput, new DoubleWordRegister(this)
264                     .WithValueField(0, NumberOfConnections, FieldMode.Read,
265                         valueProviderCallback: _ => FilterForDirection(BitHelper.GetValueFromBitsArray(State), false),
266                         name: "PDIR")
267                 },
268                 {(long)GPIORegisters.DataDirection, new DoubleWordRegister(this)
269                     .WithFlags(0, NumberOfConnections,
270                         writeCallback: (i, _, value) => interruptManager.PinDirection[i] = value ? GPIOInterruptManager.Direction.Output : GPIOInterruptManager.Direction.Input,
271                         valueProviderCallback: (i, _) => interruptManager.PinDirection[i] == GPIOInterruptManager.Direction.Output,
272                         name: "PDDR")
273                     .WithWriteCallback((_, __) => interruptManager.RefreshInterrupts())
274                 },
275                 {(long)GPIORegisters.InputDisable, new DoubleWordRegister(this)
276                     .WithFlags(0, NumberOfConnections, out inputDisabled, name: "PIDR")
277                     .WithWriteCallback((_, __) => interruptManager.RefreshInterrupts())
278                 },
279             };
280             gpioRegisters = new DoubleWordRegisterCollection(this, registers);
281         }
282 
UpdateInterrupts()283         private void UpdateInterrupts()
284         {
285             interruptManager.RefreshInterrupts();
286         }
287 
CalculateInterruptType(InterruptConfiguration type)288         private GPIOInterruptManager.InterruptTrigger CalculateInterruptType(InterruptConfiguration type)
289         {
290             switch(type)
291             {
292                 case InterruptConfiguration.InterruptWhenLow:
293                     return GPIOInterruptManager.InterruptTrigger.ActiveLow;
294                 case InterruptConfiguration.InterruptFallingEdge:
295                     return GPIOInterruptManager.InterruptTrigger.FallingEdge;
296                 case InterruptConfiguration.InterruptRisingEdge:
297                     return GPIOInterruptManager.InterruptTrigger.RisingEdge;
298                 case InterruptConfiguration.InterruptEitherEdge:
299                     return GPIOInterruptManager.InterruptTrigger.BothEdges;
300                 case InterruptConfiguration.InterruptWhenHigh:
301                     return GPIOInterruptManager.InterruptTrigger.ActiveHigh;
302                 case InterruptConfiguration.Disabled:
303                     // we have to return something, so we return ActiveLow - but it should not be relevant
304                     return GPIOInterruptManager.InterruptTrigger.ActiveLow;
305                 default:
306                     this.Log(LogLevel.Error, "Unsupported interrupt configuration: {0}", type);
307                     return GPIOInterruptManager.InterruptTrigger.ActiveLow;
308             }
309         }
310 
GlobalPinControlWrite(uint value, uint whichRegisters, bool highBits, bool highRegisters)311         private void GlobalPinControlWrite(uint value, uint whichRegisters, bool highBits, bool highRegisters)
312         {
313             var firstRegister = highRegisters ? 16 : 0;
314             var firstBit = highBits ? 16 : 0;
315             for(var i = firstRegister; i < Math.Min(16 + firstRegister, NumberOfConnections); i++)
316             {
317                 if(!BitHelper.IsBitSet(whichRegisters, (byte)(i - firstRegister)))
318                 {
319                     continue;
320                 }
321                 var currentValue = pinControl[i].Read();
322                 BitHelper.SetMaskedValue(ref currentValue, value, firstBit, 16);
323                 pinControl[i].Write((long)(Registers.PinControlRegisterStart + 0x4 * i), currentValue);
324             }
325         }
326 
FilterForDirection(uint value, bool output)327         private uint FilterForDirection(uint value, bool output)
328         {
329             var mask = BitHelper.GetValueFromBitsArray(interruptManager.PinDirection.Select(x => x == GPIOInterruptManager.Direction.Output));
330             if(!output)
331             {
332                 mask = ~mask & ~BitHelper.GetValueFromBitsArray(inputDisabled.Select(x => x.Value));
333             }
334 
335             return value & mask;
336         }
337 
338         private DoubleWordRegisterCollection gpioRegisters;
339         private DoubleWordRegisterCollection portRegisters;
340         private IFlagRegisterField[] inputDisabled;
341         private DoubleWordRegister[] pinControl;
342 
343         private readonly GPIOInterruptManager interruptManager;
344         private readonly object locker = new object();
345 
346         private enum InterruptConfiguration
347         {
348             Disabled = 0,
349             DMARequestRisingEdge = 1,
350             DMARequestFallingEdge = 2,
351             DMARequestEitherEdge = 3,
352             InterruptWhenLow = 8,
353             InterruptRisingEdge = 9,
354             InterruptFallingEdge = 10,
355             InterruptEitherEdge = 11,
356             InterruptWhenHigh = 12,
357         }
358 
359         private enum GPIORegisters
360         {
361             DataOutput = 0x00,
362             SetOutput = 0x04,
363             ClearOutput = 0x08,
364             ToggleOutput = 0x0C,
365             DataInput = 0x10,
366             DataDirection = 0x14,
367             InputDisable = 0x18
368         }
369 
370         private enum Registers
371         {
372             PinControlRegisterStart = 0x00,
373             PinControlRegisterEnd = 0x7C,
374             GlobalPinControlLow  = 0x80,
375             GlobalPinControlHigh = 0x84,
376             GlobalInterruptControlLow = 0x88,
377             GlobalInterruptControlHigh = 0x8C,
378             InterruptStatusFlag = 0xA0,
379             DigitalFilterEnable = 0xC0,
380             DigitalFilterClock = 0xC4,
381             DigitalFilterWidth = 0xC8,
382         }
383     }
384 }
385