1 //
2 // Copyright (c) 2010-2020 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.Peripherals.Bus;
13 using Antmicro.Renode.Utilities;
14 using Antmicro.Renode.Logging;
15 
16 namespace Antmicro.Renode.Peripherals.GPIOPort
17 {
18     // This model currently does not support interrupts
19     public class PULP_APB_GPIO : BaseGPIOPort, IDoubleWordPeripheral, IKnownSize
20     {
PULP_APB_GPIO(IMachine machine)21         public PULP_APB_GPIO(IMachine machine) : base(machine, NumberOfGPIOs)
22         {
23             var registersMap = new Dictionary<long, DoubleWordRegister>
24             {
25                 {(long)Registers.PadInput_00_31, new DoubleWordRegister(this)
26                     .WithFlags(0, 32, FieldMode.Read, name: "PADIN / Pad input value register",
27                         valueProviderCallback: (idx, _) => ReadInputPin(idx)
28                     )
29                 },
30                 {(long)Registers.PadInput_32_63, new DoubleWordRegister(this)
31                     .WithFlags(0, 32, FieldMode.Read, name: "PADIN / Pad input value register",
32                         valueProviderCallback: (idx, _) => ReadInputPin(idx + 32)
33                     )
34                 },
35                 {(long)Registers.PadOutput_00_31, new DoubleWordRegister(this)
36                     .WithFlags(0, 32, name: "PADOUT / Pad Output value register",
37                         writeCallback: (idx, _, val) => WriteOutputPin(idx, val, false),
38                         // Not logging on input pins, as it's not illegal to read this
39                         valueProviderCallback: (idx, _) => gpioDirection[idx] == Direction.Out ? Connections[idx].IsSet : false
40                     )
41                 },
42                 {(long)Registers.PadOutputSet_00_31, new DoubleWordRegister(this)
43                     .WithFlags(0, 32, FieldMode.Write, name: "PADOUTSET / Pad Output set register",
44                         writeCallback: (idx, _, val) =>
45                         {
46                             if(val)
47                             {
48                                 WriteOutputPin(idx, val, true);
49                             }
50                         }
51                     )
52                 },
53                 {(long)Registers.PadOutputClear_00_31, new DoubleWordRegister(this)
54                     .WithFlags(0, 32, FieldMode.Write, name: "PADOUTCLR / Pad Output clear register",
55                         writeCallback: (idx, _, val) =>
56                         {
57                             if(val)
58                             {
59                                 WriteOutputPin(idx, !val, true);
60                             }
61                         }
62                     )
63                 },
64                 {(long)Registers.PadOutput_32_63, new DoubleWordRegister(this)
65                     .WithFlags(0, 32, name: "PADOUT / Pad Output value register",
66                         writeCallback: (idx, _, val) => WriteOutputPin(idx + 32, val, false),
67                         // Not logging on input pins, as it's not illegal to read this
68                         valueProviderCallback: (idx, _) => gpioDirection[idx + 32] == Direction.Out ? Connections[idx + 32].IsSet : false
69                     )
70                 },
71                 {(long)Registers.PadOutputSet_32_63, new DoubleWordRegister(this)
72                     .WithFlags(0, 32, FieldMode.Write, name: "PADOUTSET / Pad Output set register",
73                         writeCallback: (idx, _, val) =>
74                         {
75                             if(val)
76                             {
77                                 WriteOutputPin(idx + 32, val, true);
78                             }
79                         }
80                     )
81                 },
82                 {(long)Registers.PadOutputClear_32_63, new DoubleWordRegister(this)
83                     .WithFlags(0, 32, FieldMode.Write, name: "PADOUTCLR / Pad Output clear register",
84                         writeCallback: (idx, _, val) =>
85                         {
86                             if(val)
87                             {
88                                 WriteOutputPin(idx + 32, !val, true);
89                             }
90                         }
91                     )
92                 },
93                 {(long)Registers.GpioEnable_00_31, new DoubleWordRegister(this)
94                     .WithFlags(0, 32, name: "GPIOEN / GPIO enable register",
95                         writeCallback: (idx, _, val) => gpioClockEnabled[idx] = val,
96                         valueProviderCallback: (idx, _) => gpioClockEnabled[idx]
97                     )
98                 },
99                 {(long)Registers.GpioEnable_32_63, new DoubleWordRegister(this)
100                     .WithFlags(0, 32, name: "GPIOEN / GPIO enable register",
101                         writeCallback: (idx, _, val) => gpioClockEnabled[idx + 32] = val,
102                         valueProviderCallback: (idx, _) => gpioClockEnabled[idx + 32]
103                     )
104                 },
105                 {(long)Registers.PadDirection_00_31, new DoubleWordRegister(this)
106                     .WithFlags(0, 32, name: "PADDIR / GPIO pad direction configuration register",
107                         writeCallback: (idx, _, val) => gpioDirection[idx] = val ? Direction.Out : Direction.In,
108                         valueProviderCallback: (idx, _) => gpioDirection[idx] == Direction.Out
109                     )
110                 },
111                 {(long)Registers.PadDirection_32_63, new DoubleWordRegister(this)
112                     .WithFlags(0, 32, name: "PADDIR / GPIO pad direction configuration register",
113                         writeCallback: (idx, _, val) => gpioDirection[idx + 32] = val ? Direction.Out : Direction.In,
114                         valueProviderCallback: (idx, _) => gpioDirection[idx + 32] == Direction.Out
115                     )
116                 },
117             };
118 
119             registers = new DoubleWordRegisterCollection(this, registersMap);
120         }
121 
ReadDoubleWord(long offset)122         public uint ReadDoubleWord(long offset)
123         {
124             return registers.Read(offset);
125         }
126 
WriteDoubleWord(long offset, uint value)127         public void WriteDoubleWord(long offset, uint value)
128         {
129             registers.Write(offset, value);
130         }
131 
Reset()132         public override void Reset()
133         {
134             base.Reset();
135             registers.Reset();
136             for(var i = 0; i < NumberOfGPIOs; ++i)
137             {
138                 gpioClockEnabled[i] = false;
139                 gpioDirection[i] = Direction.In;
140             }
141         }
142 
ReadInputPin(int pin)143         private bool ReadInputPin(int pin)
144         {
145             if(gpioDirection[pin] != Direction.In)
146             {
147                 // Trying to read pin that is not configured as input
148                 return false;
149             }
150             if(!gpioClockEnabled[pin])
151             {
152                 // According to docs, clock gating is effective when you gate 4 pins at once. We don't simulate this here
153                 this.Log(LogLevel.Noisy, "Trying to read pin #{0} that has clock disabled", pin);
154                 return false;
155             }
156 
157             return State[pin];
158         }
159 
WriteOutputPin(int pin, bool val, bool isExplicit)160         private void WriteOutputPin(int pin, bool val, bool isExplicit)
161         {
162             if(gpioDirection[pin] == Direction.Out)
163             {
164                 Connections[pin].Set(val);
165             }
166             else if(isExplicit)
167             {
168                 // Log only if the driver specifically affets this pin and it's not properly configured
169                 this.Log(LogLevel.Noisy, "Trying to write to pin #{pin} that is not configured as output");
170             }
171         }
172 
173         public long Size => 0x1000;
174 
175         private const int NumberOfGPIOs = 64;
176         private bool[] gpioClockEnabled = new bool[NumberOfGPIOs];
177         private Direction[] gpioDirection = new Direction[NumberOfGPIOs];
178 
179         private readonly DoubleWordRegisterCollection registers;
180 
181         public enum Direction
182         {
183             In,
184             Out,
185         };
186 
187         private enum Registers
188         {
189             PadDirection_00_31 = 0x0,
190             GpioEnable_00_31 = 0x4,
191             PadInput_00_31 = 0x8,
192             PadOutput_00_31 = 0xC,
193             PadOutputSet_00_31 = 0x10,
194             PadOutputClear_00_31 = 0x14,
195             InterruptEnable_00_31 = 0x18,
196             InterruptType_00_15 = 0x1C,
197             InterruptType_16_31 = 0x20,
198             InterruptStatus_00_31 = 0x24,
199             PadConfiguration_00_07 = 0x28,
200             PadConfiguration_08_15 = 0x2C,
201             PadConfiguration_16_23 = 0x30,
202             PadConfiguration_24_31 = 0x34,
203             PadDirection_32_63 = 0x38,
204             GpioEnable_32_63 = 0x3c,
205             PadInput_32_63 = 0x40,
206             PadOutput_32_63 = 0x44,
207             PadOutputSet_32_63 = 0x48,
208             PadOutputClear_32_63 = 0x4c,
209             InterruptEnable_32_63 = 0x50,
210             InterruptType_32_47 = 0x54,
211             InterruptType_48_63 = 0x58,
212             InterruptStatus_32_63 = 0x5c,
213             PadConfiguration_32_39 = 0x60,
214             PadConfiguration_40_47 = 0x64,
215             PadConfiguration_48_55 = 0x68,
216             PadConfiguration_56_63 = 0x6c,
217         }
218     }
219 }
220 
221