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.Collections.Generic;
9 using System.Linq;
10 using Antmicro.Renode.Core;
11 using Antmicro.Renode.Core.Structure.Registers;
12 using Antmicro.Renode.Exceptions;
13 using Antmicro.Renode.Logging;
14 using Antmicro.Renode.Peripherals.Bus;
15 using Antmicro.Renode.Utilities;
16 using Antmicro.Renode.Utilities.Collections;
17 
18 namespace Antmicro.Renode.Peripherals.GPIOPort
19 {
20     [AllowedTranslations(AllowedTranslation.ByteToDoubleWord)]
21     public class MiV_CoreGPIO : BaseGPIOPort, IDoubleWordPeripheral, IKnownSize
22     {
MiV_CoreGPIO(IMachine machine)23         public MiV_CoreGPIO(IMachine machine) : base(machine, NumberOfInterrupts)
24         {
25             innerLock = new object();
26             IRQ = new GPIO();
27             fixedDirection = new bool[NumberOfConnections];
28             fixedIrqTriggerType = new bool[NumberOfConnections];
29 
30             irqManager = new GPIOInterruptManager(IRQ, State);
31 
32             var registersMap = new Dictionary<long, DoubleWordRegister>
33             {
34                 {(long)Registers.InterruptClearRegister, new DoubleWordRegister(this).WithValueField(0, 32, FieldMode.Write,
35                     writeCallback: (_, value) =>
36                     {
37                         lock(innerLock)
38                         {
39                             foreach(var i in BitHelper.GetSetBits(value))
40                             {
41                                 irqManager.ClearInterrupt(i);
42                             }
43                         }
44                     })},
45 
46                 {(long)Registers.InputRegister, new DoubleWordRegister(this).WithValueField(0, 32, FieldMode.Read,
47                     valueProviderCallback: _ =>
48                     {
49                         lock(innerLock)
50                         {
51                             return GetConnectedInputPinsState();
52                         }
53                     })},
54 
55                 {(long)Registers.OutputRegister, new DoubleWordRegister(this).WithValueField(0, 32,
56                     writeCallback: (_, value) =>
57                     {
58                         lock(innerLock)
59                         {
60                             var bits = BitHelper.GetBits((uint)value);
61                             for(var i = 0; i < bits.Length; i++)
62                             {
63                                 if((irqManager.PinDirection[i] & GPIOInterruptManager.Direction.Output) != 0)
64                                 {
65                                     Connections[i].Set(bits[i]);
66                                 }
67                             }
68                         }
69                     }, valueProviderCallback: _ =>
70                     {
71                         lock(innerLock)
72                         {
73                             return GetConnectedOutputPinsState();
74                         }
75                     })}
76             };
77 
78             intTypeToVal = new TwoWayDictionary<GPIOInterruptManager.InterruptTrigger, uint>();
79             intTypeToVal.Add(GPIOInterruptManager.InterruptTrigger.ActiveHigh, 0);
80             intTypeToVal.Add(GPIOInterruptManager.InterruptTrigger.ActiveLow, 1);
81             intTypeToVal.Add(GPIOInterruptManager.InterruptTrigger.RisingEdge, 2);
82             intTypeToVal.Add(GPIOInterruptManager.InterruptTrigger.FallingEdge, 3);
83             intTypeToVal.Add(GPIOInterruptManager.InterruptTrigger.BothEdges, 4);
84 
85             for(var i = 0; i < NumberOfInterrupts; i++)
86             {
87                 var j = i;
88                 registersMap.Add((long)Registers.ConfigurationRegisterBase + i * 0x4, new DoubleWordRegister(this)
89                     .WithFlag(0,
90                         writeCallback: (_, v) =>
91                         {
92                             if(fixedDirection[j])
93                             {
94                                 this.Log(LogLevel.Warning, "Cannot change pin #{0} direction because it is fixed");
95                                 return;
96                             }
97 
98                             if(v)
99                             {
100                                 irqManager.PinDirection[j] |= GPIOInterruptManager.Direction.Output;
101                             }
102                             else
103                             {
104                                 irqManager.PinDirection[j] &= ~GPIOInterruptManager.Direction.Output;
105                             }
106                         },
107                         valueProviderCallback: _ =>
108                         {
109                             return (irqManager.PinDirection[j] & GPIOInterruptManager.Direction.Output) != 0;
110                         }, name: "OUTREG")
111                     .WithFlag(1,
112                         writeCallback: (_, value) =>
113                         {
114                             if(fixedDirection[j])
115                             {
116                                 this.Log(LogLevel.Warning, "Cannot change pin #{0} direction because it is fixed");
117                                 return;
118                             }
119 
120                             if(value)
121                             {
122                                 irqManager.PinDirection[j] |= GPIOInterruptManager.Direction.Input;
123                             }
124                             else
125                             {
126                                 irqManager.PinDirection[j] &= ~GPIOInterruptManager.Direction.Input;
127                             }
128                         },
129                         valueProviderCallback: _ =>
130                         {
131                             return (irqManager.PinDirection[j] & GPIOInterruptManager.Direction.Input) != 0;
132                         }, name: "INREG")
133                     .WithFlag(2, name: "OUTBUFF") // The register only provides a read-back function
134                     .WithFlag(3, writeCallback: (_, v) => irqManager.InterruptEnable[j] = v, valueProviderCallback: _ => irqManager.InterruptEnable[j], name: "INTENABLE")
135                     .WithReservedBits(4, 1)
136                     .WithValueField(5, 3, writeCallback: (_, value) =>
137                     {
138                         if(fixedIrqTriggerType[j])
139                         {
140                             this.Log(LogLevel.Warning, "Cannot change pin #{0} interrupt type because it is fixed");
141                             return;
142                         }
143 
144                         if(!intTypeToVal.TryGetValue((uint)value, out var type))
145                         {
146                             this.Log(LogLevel.Warning, "Invalid interrupt type for pin #{0}: {1}", j, value);
147                             return;
148                         }
149                         irqManager.InterruptType[j] = type;
150                     }, valueProviderCallback: _ =>
151                     {
152                         if(!intTypeToVal.TryGetValue(irqManager.InterruptType[j], out var value))
153                         {
154                             throw new ArgumentOutOfRangeException($"Unknown interrupt trigger type: {irqManager.InterruptType[j]}");
155                         }
156                         return value;
157                     }, name: "INTTYPE"));
158             }
159 
160             registers = new DoubleWordRegisterCollection(this, registersMap);
161         }
162 
ReadDoubleWord(long offset)163         public uint ReadDoubleWord(long offset)
164         {
165             return registers.Read(offset);
166         }
167 
WriteDoubleWord(long offset, uint value)168         public void WriteDoubleWord(long offset, uint value)
169         {
170             registers.Write(offset, value);
171         }
172 
OnGPIO(int number, bool value)173         public override void OnGPIO(int number, bool value)
174         {
175             lock(innerLock)
176             {
177                 base.OnGPIO(number, value);
178                 if((irqManager.PinDirection[number] & GPIOInterruptManager.Direction.Input) == 0)
179                 {
180                     this.Log(LogLevel.Warning, "Writing to an output GPIO pin #{0}", number);
181                     return;
182                 }
183                 irqManager.RefreshInterrupts();
184             }
185         }
186 
Reset()187         public override void Reset()
188         {
189             lock(innerLock)
190             {
191                 base.Reset();
192                 irqManager.Reset();
193                 registers.Reset();
194                 for(var i = 0; i < NumberOfConnections; ++i)
195                 {
196                     fixedDirection[i] = false;
197                     fixedIrqTriggerType[i] = false;
198                 }
199             }
200         }
201 
ConfigureFixedPinTriggerMode(int number, GPIOInterruptManager.InterruptTrigger triggerType)202         public void ConfigureFixedPinTriggerMode(int number, GPIOInterruptManager.InterruptTrigger triggerType)
203         {
204             if(!intTypeToVal.TryGetValue(triggerType, out var value))
205             {
206                 throw new RecoverableException("Invalid interrupt type for pin #{number}: {type}");
207             }
208             irqManager.InterruptType[number] = triggerType;
209             fixedIrqTriggerType[number] = true;
210         }
211 
ConfigureFixedPinDirection(int number, PinDirection direction)212         public void ConfigureFixedPinDirection(int number, PinDirection direction)
213         {
214             switch(direction)
215             {
216                 case PinDirection.Input:
217                     irqManager.PinDirection[number] = GPIOInterruptManager.Direction.Input;
218                     break;
219                 case PinDirection.Output:
220                     irqManager.PinDirection[number] = GPIOInterruptManager.Direction.Output;
221                     break;
222                 case PinDirection.Bidirectional:
223                     irqManager.PinDirection[number] =
224                         GPIOInterruptManager.Direction.Input | GPIOInterruptManager.Direction.Output;
225                     break;
226                 default:
227                     throw new RecoverableException("Invalid option of GPIO direction: {direction}");
228             }
229             fixedDirection[number] = true;
230         }
231 
232         public GPIO IRQ { get; }
233 
234         public long Size => 0xA4;
235 
236         public enum PinDirection : int
237         {
238             Input = 0,
239             Output = 1,
240             Bidirectional = 2
241         }
242 
GetConnectedInputPinsState()243         private uint GetConnectedInputPinsState()
244         {
245             var pins = irqManager.PinDirection.Select(x => (x & GPIOInterruptManager.Direction.Input) != 0);
246             var result = pins.Zip(State, (pin, state) => pin && state);
247             return BitHelper.GetValueFromBitsArray(result);
248         }
249 
GetConnectedOutputPinsState()250         private uint GetConnectedOutputPinsState()
251         {
252             var pins = irqManager.PinDirection.Select(x => (x & GPIOInterruptManager.Direction.Output) != 0);
253             var result = pins.Zip(Connections.Values, (pin, state) => pin && state.IsSet);
254             return BitHelper.GetValueFromBitsArray(result);
255         }
256 
257         private readonly GPIOInterruptManager irqManager;
258         private readonly DoubleWordRegisterCollection registers;
259         private readonly TwoWayDictionary<GPIOInterruptManager.InterruptTrigger, uint> intTypeToVal;
260         private readonly bool[] fixedDirection;
261         private readonly bool[] fixedIrqTriggerType;
262         private readonly object innerLock;
263 
264         private const int NumberOfInterrupts = 32;
265         private enum Registers : long
266         {
267             ConfigurationRegisterBase = 0x0,
268             InterruptClearRegister = 0x80,
269             InputRegister = 0x90,
270             OutputRegister = 0xA0
271         }
272     }
273 }
274