1 //
2 // Copyright (c) 2010-2022 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.Collections.Generic;
8 using Antmicro.Renode.Core;
9 using Antmicro.Renode.Utilities;
10 using Antmicro.Renode.Peripherals.Bus;
11 using Antmicro.Renode.Core.Structure.Registers;
12 
13 namespace Antmicro.Renode.Peripherals.GPIOPort
14 {
15     public class OpenTitan_GPIO : BaseGPIOPort, IDoubleWordPeripheral, IKnownSize
16     {
OpenTitan_GPIO(IMachine machine)17         public OpenTitan_GPIO(IMachine machine) : base(machine, numberOfPins)
18         {
19             locker = new object();
20             IRQ = new GPIO();
21             FatalAlert = new GPIO();
22             registers = new DoubleWordRegisterCollection(this, BuildRegisterMap());
23             interruptRequest = new bool[numberOfPins];
24             interruptEnabled = new bool[numberOfPins];
25             directOutputValue = new bool[numberOfPins];
26             maskedOutputValue = new bool[numberOfPins];
27             directOutputEnabled = new bool[numberOfPins];
28             maskedOutputEnabled = new bool[numberOfPins];
29             interruptEnableRising = new bool[numberOfPins];
30             interruptEnableFalling = new bool[numberOfPins];
31             interruptEnableHigh = new bool[numberOfPins];
32             interruptEnableLow = new bool[numberOfPins];
33         }
34 
Reset()35         public override void Reset()
36         {
37             lock(locker)
38             {
39                 base.Reset();
40                 IRQ.Unset();
41                 FatalAlert.Unset();
42 
43                 registers.Reset();
44                 for(var i = 0; i < numberOfPins; ++i)
45                 {
46                     interruptRequest[i] = false;
47                     interruptEnabled[i] = false;
48                     directOutputValue[i] = false;
49                     maskedOutputValue[i] = false;
50                     directOutputEnabled[i] = false;
51                     maskedOutputEnabled[i] = false;
52                     interruptEnableRising[i] = false;
53                     interruptEnableFalling[i] = false;
54                     interruptEnableHigh[i] = false;
55                     interruptEnableLow[i] = false;
56                 }
57             }
58         }
59 
ReadDoubleWord(long offset)60         public uint ReadDoubleWord(long offset)
61         {
62             lock(locker)
63             {
64                 return registers.Read(offset);
65             }
66         }
67 
WriteDoubleWord(long offset, uint value)68         public void WriteDoubleWord(long offset, uint value)
69         {
70             lock(locker)
71             {
72                 registers.Write(offset, value);
73             }
74         }
75 
OnGPIO(int number, bool value)76         public override void OnGPIO(int number, bool value)
77         {
78             if(!CheckPinNumber(number))
79             {
80                 return;
81             }
82 
83             lock(locker)
84             {
85                 var previousState = GetStateOnInput(number);
86                 base.OnGPIO(number, value);
87 
88                 if(interruptEnabled[number])
89                 {
90                     var currentState = GetStateOnInput(number);
91                     if(interruptEnableRising[number])
92                     {
93                         interruptRequest[number] |= !previousState && currentState;
94                     }
95                     if(interruptEnableFalling[number])
96                     {
97                         interruptRequest[number] |= previousState && !currentState;
98                     }
99                 }
100 
101                 UpdateIRQ();
102             }
103         }
104 
105         public long Size => 0x3C;
106 
107         public GPIO IRQ { get; }
108         public GPIO FatalAlert { get; }
109 
BuildRegisterMap()110         private Dictionary<long, DoubleWordRegister> BuildRegisterMap()
111         {
112             return new Dictionary<long, DoubleWordRegister>
113             {
114                 {(long)Registers.InterruptState, new DoubleWordRegister(this)
115                     .WithFlags(0, 32, FieldMode.Read | FieldMode.WriteOneToClear, name: "INTR_STATE",
116                         valueProviderCallback: (id, _) => interruptRequest[id],
117                         writeCallback: (id, _, val) => { if(val) interruptRequest[id] = false; })
118                     .WithWriteCallback((_, __) => UpdateIRQ())
119                 },
120                 {(long)Registers.InterruptEnable, new DoubleWordRegister(this)
121                     .WithFlags(0, 32, name: "INTR_ENABLE",
122                         valueProviderCallback: (id, _) => interruptEnabled[id],
123                         writeCallback: (id, _, val) => { interruptEnabled[id] = val; })
124                     .WithWriteCallback((_, __) => UpdateIRQ())
125                 },
126                 {(long)Registers.InterruptTest, new DoubleWordRegister(this)
127                     .WithFlags(0, 32, FieldMode.Write, name: "INTR_TEST",
128                         writeCallback: (id, _, val) => { interruptRequest[id] |= val; })
129                     .WithWriteCallback((_, __) => UpdateIRQ())
130                 },
131                 {(long)Registers.AlertTest, new DoubleWordRegister(this)
132                     .WithFlag(0, FieldMode.Write, writeCallback: (_, val) => { if(val) FatalAlert.Blink(); }, name: "fatal_fault")
133                     .WithIgnoredBits(1, 31)
134                 },
135                 {(long)Registers.Input, new DoubleWordRegister(this)
136                     .WithFlags(0, 32, FieldMode.Read, name: "DATA_IN",
137                         valueProviderCallback: (id, _) => GetStateOnInput(id))
138                 },
139                 {(long)Registers.Output, new DoubleWordRegister(this)
140                     .WithFlags(0, 32, name: "DIRECT_OUT",
141                         valueProviderCallback: (id, _) => directOutputValue[id],
142                         writeCallback: (id, _, val) => { directOutputValue[id] = val; })
143                     .WithWriteCallback((_, __) => UpdateConnections())
144                 },
145                 {(long)Registers.OutputMaskedLower, new DoubleWordRegister(this)
146                     .WithFlags(0, 16, name: "MASKED_OUT_LOWER.data",
147                         valueProviderCallback: (id, _) => Connections[id].IsSet)
148                     .WithFlags(16, 16, FieldMode.Write, name: "MASKED_OUT_LOWER.mask")
149                     .WithWriteCallback((_, val) => SetOutputMasked(val, lower: true))
150                 },
151                 {(long)Registers.OutputMaskedUpper, new DoubleWordRegister(this)
152                     .WithFlags(0, 16, name: "MASKED_OUT_UPPER.data",
153                         valueProviderCallback: (id, _) => Connections[id + 16].IsSet)
154                     .WithFlags(16, 16, FieldMode.Write, name: "MASKED_OUT_UPPER.mask")
155                     .WithWriteCallback((_, val) => SetOutputMasked(val, lower: false))
156                 },
157                 {(long)Registers.OutputEnable, new DoubleWordRegister(this)
158                     .WithFlags(0, 32, name: "DIRECT_OE",
159                         valueProviderCallback: (id, _) => directOutputEnabled[id],
160                         writeCallback: (id, _, val) => { directOutputEnabled[id] = val; })
161                     .WithWriteCallback((_, __) => UpdateConnections())
162                 },
163                 {(long)Registers.OutputEnableMaskedLower, new DoubleWordRegister(this)
164                     .WithFlags(0, 16, name: "MASKED_OE_LOWER.data",
165                         valueProviderCallback: (id, _) => maskedOutputEnabled[id])
166                     .WithFlags(16, 16, FieldMode.Write, name: "MASKED_OE_LOWER.mask")
167                     .WithWriteCallback((_, val) => SetOutputEnableMasked(val, lower: true))
168                 },
169                 {(long)Registers.OutputEnableMaskedUpper, new DoubleWordRegister(this)
170                     .WithFlags(0, 16, name: "MASKED_OE_UPPER.data",
171                         valueProviderCallback: (id, _) => maskedOutputEnabled[id + 16])
172                     .WithFlags(16, 16, FieldMode.Write, name: "MASKED_OE_UPPER.mask")
173                     .WithWriteCallback((_, val) => SetOutputEnableMasked(val, lower: false))
174                 },
175                 {(long)Registers.InterruptEnableRising, new DoubleWordRegister(this)
176                     .WithFlags(0, 32, name: "INTR_CTRL_EN_RISING",
177                         writeCallback: (id, _, val) => { interruptEnableRising[id] = val; },
178                         valueProviderCallback: (id, _) => interruptEnableRising[id])
179                 },
180                 {(long)Registers.InterruptEnableFalling, new DoubleWordRegister(this)
181                     .WithFlags(0, 32, name: "INTR_CTRL_EN_FALLING",
182                         writeCallback: (id, _, val) => { interruptEnableFalling[id] = val; },
183                         valueProviderCallback: (id, _) => interruptEnableFalling[id])
184                 },
185                 {(long)Registers.InterruptEnableHigh, new DoubleWordRegister(this)
186                     .WithFlags(0, 32, name: "INTR_CTRL_EN_LVLHIGH",
187                         writeCallback: (id, _, val) => { interruptEnableHigh[id] = val; },
188                         valueProviderCallback: (id, _) => interruptEnableHigh[id])
189                     .WithWriteCallback((_, __) => UpdateIRQ())
190                 },
191                 {(long)Registers.InterruptEnableLow, new DoubleWordRegister(this)
192                     .WithFlags(0, 32, name: "INTR_CTRL_EN_LVLLOW",
193                         writeCallback: (id, _, val) => { interruptEnableLow[id] = val; },
194                         valueProviderCallback: (id, _) => interruptEnableLow[id])
195                     .WithWriteCallback((_, __) => UpdateIRQ())
196                 }
197             };
198         }
199 
UpdateIRQ()200         private void UpdateIRQ()
201         {
202             var flag = false;
203             for(var i = 0; i < numberOfPins; ++i)
204             {
205                 if(!interruptEnabled[i])
206                 {
207                     continue;
208                 }
209                 var state = GetStateOnInput(i);
210                 interruptRequest[i] |= state ? interruptEnableHigh[i] : interruptEnableLow[i];
211                 flag |= interruptRequest[i];
212             }
213             IRQ.Set(flag);
214         }
215 
UpdateConnections()216         private void UpdateConnections()
217         {
218             for(var i = 0; i < numberOfPins; ++i)
219             {
220                 if(directOutputEnabled[i])
221                 {
222                     Connections[i].Set(directOutputValue[i]);
223                 }
224                 else if(maskedOutputEnabled[i])
225                 {
226                     Connections[i].Set(maskedOutputValue[i]);
227                 }
228                 else
229                 {
230                     Connections[i].Set(false);
231                 }
232             }
233             UpdateIRQ();
234         }
235 
SetOutputMasked(uint value, bool lower)236         private void SetOutputMasked(uint value, bool lower)
237         {
238             var offset = lower ? 0 : 16;
239             var data = BitHelper.GetBits(value & 0xFFFF);
240             var mask = BitHelper.GetBits(value >> 16);
241             for(var i = 0; i < 16; ++i)
242             {
243                 if(mask[i])
244                 {
245                     maskedOutputValue[i + offset] = data[i];
246                 }
247             }
248             UpdateConnections();
249         }
250 
SetOutputEnableMasked(uint value, bool lower)251         private void SetOutputEnableMasked(uint value, bool lower)
252         {
253             var offset = lower ? 0 : 16;
254             var data = BitHelper.GetBits(value & 0xFFFF);
255             var mask = BitHelper.GetBits(value >> 16);
256             for(var i = 0; i < 16; ++i)
257             {
258                 if(mask[i])
259                 {
260                     directOutputEnabled[i + offset] = data[i];
261                 }
262             }
263             UpdateConnections();
264         }
265 
GetStateOnInput(int i)266         private bool GetStateOnInput(int i)
267         {
268             // Output and Input are physically connected
269             return State[i] || Connections[i].IsSet;
270         }
271 
272         private readonly DoubleWordRegisterCollection registers;
273         private readonly object locker;
274         private bool[] interruptRequest;
275         private bool[] interruptEnabled;
276         private bool[] directOutputValue;
277         private bool[] maskedOutputValue;
278         private bool[] directOutputEnabled;
279         private bool[] maskedOutputEnabled;
280         private bool[] interruptEnableRising;
281         private bool[] interruptEnableFalling;
282         private bool[] interruptEnableHigh;
283         private bool[] interruptEnableLow;
284 
285         private const int numberOfPins = 32;
286 
287         private enum Registers : long
288         {
289             InterruptState          = 0x0,
290             InterruptEnable         = 0x4,
291             InterruptTest           = 0x8,
292             AlertTest               = 0xC,
293             Input                   = 0x10,
294             Output                  = 0x14,
295             OutputMaskedLower       = 0x18,
296             OutputMaskedUpper       = 0x1C,
297             OutputEnable            = 0x20,
298             OutputEnableMaskedLower = 0x24,
299             OutputEnableMaskedUpper = 0x28,
300             InterruptEnableRising   = 0x2C,
301             InterruptEnableFalling  = 0x30,
302             InterruptEnableHigh     = 0x34,
303             InterruptEnableLow      = 0x38,
304             InputFilter             = 0x3C
305         }
306     }
307 }
308