1 //
2 // Copyright (c) 2010-2023 Antmicro
3 // Copyright (c) 2011-2015 Realtime Embedded
4 //
5 // This file is licensed under the MIT License.
6 // Full license text is available in 'licenses/MIT.txt'.
7 //
8 using System;
9 using Antmicro.Renode.Peripherals.Bus;
10 using Antmicro.Renode.Peripherals.Miscellaneous;
11 using Antmicro.Renode.Core;
12 using Antmicro.Renode.Logging;
13 
14 namespace Antmicro.Renode.Peripherals.GPIOPort
15 {
16     public class XilinxGPIOPS : BaseGPIOPort, IDoubleWordPeripheral, IKnownSize
17     {
XilinxGPIOPS(IMachine machine, uint numberOfGpioBanks = 4)18         public XilinxGPIOPS(IMachine machine, uint numberOfGpioBanks = 4) : base (machine, 54 + 64) //54 MIO + 64 EMIO
19         {
20             portControllers = new GPIOController[numberOfGpioBanks];
21             for(uint i = 0; i < numberOfGpioBanks; i++)
22             {
23                 portControllers[i] = new GPIOController(this, i);
24             }
25         }
26 
ReadDoubleWord(long offset)27         public uint ReadDoubleWord(long offset)
28         {
29             if(offset > 0x200)
30             {
31                 var portNumber = (uint)((offset - 0x200) / 0x40);
32                 return portControllers[portNumber].ReadRegister(offset % 0x40);
33             }
34             switch((registersOffsets)offset)
35             {
36             case registersOffsets.MaskableOutputData0Low:
37                 return OutputData0 & 0xFFFF;
38             case registersOffsets.MaskableOutputData0Hi:
39                 return OutputData0 >> 16;
40             case registersOffsets.MaskableOutputData1Low:
41                 return OutputData1 & 0xFFFF;
42             case registersOffsets.MaskableOutputData1Hi:
43                 return OutputData1 >> 16;
44             case registersOffsets.MaskableOutputData2Low:
45                 return OutputData2 & 0xFFFF;
46             case registersOffsets.MaskableOutputData2Hi:
47                 return OutputData2 >> 16;
48             case registersOffsets.MaskableOutputData3Low:
49                 return OutputData3 & 0xFFFF;
50             case registersOffsets.MaskableOutputData3Hi:
51                 return OutputData3 >> 16;
52             case registersOffsets.OutputData0:
53                 return OutputData0;
54             case registersOffsets.OutputData1:
55                 return OutputData1;
56             case registersOffsets.OutputData2:
57                 return OutputData2;
58             case registersOffsets.OutputData3:
59                 return OutputData3;
60             case registersOffsets.InputData0:
61                 return 0;
62             case registersOffsets.InputData1:
63                 return 0;
64             case registersOffsets.InputData2:
65                 return 0;
66             case registersOffsets.InputData3:
67                 return 0;
68             default:
69                 this.LogUnhandledRead(offset);
70                 return 0;
71             }
72         }
73 
WriteDoubleWord(long offset, uint value)74         public void WriteDoubleWord(long offset, uint value)
75         {
76             if(offset > 0x200)
77             {
78                 var portNumber = (uint)((offset - 0x200) / 0x40);
79                 portControllers[portNumber].WriteRegister(offset % 0x40, value);
80                 return;
81             }
82             switch((registersOffsets)offset)
83             {
84             case registersOffsets.MaskableOutputData0Low:
85                 OutputData0 = (OutputData0 & 0xFFFF0000) | (value & 0xFFFF);
86                 this.DoPinOperation(0, value & 0xFFFF, 0xFFFF0000 | value >> 16);
87                 break;
88             case registersOffsets.MaskableOutputData0Hi:
89                 OutputData0 = (OutputData0 & 0x0000FFFF) | (value << 16);
90                 this.DoPinOperation(0, value & 0xFFFF, 0x0000FFFF | value >> 16);
91                 break;
92             case registersOffsets.MaskableOutputData1Low:
93                 OutputData1 = (OutputData1 & 0xFFFF0000) | (value & 0xFFFF);
94                 this.DoPinOperation(1, value & 0xFFFF, 0xFFFF0000 | value >> 16);
95                 break;
96             case registersOffsets.MaskableOutputData1Hi:
97                 OutputData1 = (OutputData1 & 0x0000FFFF) | (value << 16);
98                 this.DoPinOperation(1, value & 0xFFFF, 0x0000FFFF | value >> 16);
99                 break;
100             case registersOffsets.MaskableOutputData2Low:
101                 OutputData2 = (OutputData2 & 0xFFFF0000) | (value & 0xFFFF);
102                 this.DoPinOperation(2, value & 0xFFFF, 0xFFFF0000 | value >> 16);
103                 break;
104             case registersOffsets.MaskableOutputData2Hi:
105                 OutputData2 = (OutputData2 & 0x0000FFFF) | (value << 16);
106                 this.DoPinOperation(2, value & 0xFFFF, 0x0000FFFF | value >> 16);
107                 break;
108             case registersOffsets.MaskableOutputData3Low:
109                 OutputData3 = (OutputData3 & 0xFFFF0000) | (value & 0xFFFF);
110                 this.DoPinOperation(3, value & 0xFFFF, 0xFFFF0000 | value >> 16);
111                 break;
112             case registersOffsets.MaskableOutputData3Hi:
113                 OutputData3 = (OutputData3 & 0x0000FFFF) | (value << 16);
114                 this.DoPinOperation(3, value & 0xFFFF, 0x0000FFFF | value >> 16);
115                 break;
116             case registersOffsets.OutputData0:
117                 OutputData0 = value;
118                 this.DoPinOperation(0, value, 0);
119                 break;
120             case registersOffsets.OutputData1:
121                 OutputData1 = value;
122                 this.DoPinOperation(1, value, 0);
123                 break;
124             case registersOffsets.OutputData2:
125                 OutputData2 = value;
126                 this.DoPinOperation(2, value, 0);
127                 break;
128             case registersOffsets.OutputData3:
129                 OutputData2 = value;
130                 this.DoPinOperation(2, value, 0);
131                 break;
132             case registersOffsets.InputData0:
133                 this.Log(LogLevel.Warning, "Writing read only register offset: {0:X} value: {1:X}", offset, value);
134                 break;
135             case registersOffsets.InputData1:
136                 this.Log(LogLevel.Warning, "Writing read only register offset: {0:X} value: {1:X}", offset, value);
137                 break;
138             case registersOffsets.InputData2:
139                 this.Log(LogLevel.Warning, "Writing read only register offset: {0:X} value: {1:X}", offset, value);
140                 break;
141             case registersOffsets.InputData3:
142                 this.Log(LogLevel.Warning, "Writing read only register offset: {0:X} value: {1:X}", offset, value);
143                 break;
144             default:
145                 this.LogUnhandledWrite(offset, value);
146                 break;
147             }
148         }
149 
DoPinOperation(int portNumber, uint value, uint mask)150         private void DoPinOperation(int portNumber, uint value, uint mask)
151         {
152             /* Port 1 is only 22 bit long, while all the others are 32 bit*/
153             var portLength = portNumber == 1 ? 21 : 31;
154             var outputEnabled = portControllers[portNumber].OutputEnabled();
155             for(int i = 0; i < portLength; i++)
156             {
157                 if((mask & (1u << i)) == 0)
158                 {
159                     if((outputEnabled & (1u << i)) != 0)
160                     {
161                         if((value & (1u << i)) != 0)
162                         {
163                             Connections[(int)portOffsets[portNumber] + i].Set();
164                         }
165                         else
166                         {
167                             Connections[(int)portOffsets[portNumber] + i].Unset();
168                         }
169                     }
170                 }
171             }
172         }
173 
174         public long Size
175         {
176             get
177             {
178                 return 0x2E8;
179             }
180         }
181 
182         private GPIOController[] portControllers;
183 
184         /* Registers */
185         private uint OutputData0;
186         private uint OutputData1;
187         private uint OutputData2;
188         private uint OutputData3;
189         /* For the future use
190         private uint InputData0;
191         private uint InputData1;
192         private uint InputData2;
193         private uint InputData3;
194         */
195         /* Offsets */
196         private enum registersOffsets : uint
197         {
198             MaskableOutputData0Low = 0x00,
199             MaskableOutputData0Hi = 0x04,
200             MaskableOutputData1Low = 0x08,
201             MaskableOutputData1Hi = 0x0C,
202             MaskableOutputData2Low = 0x10,
203             MaskableOutputData2Hi = 0x14,
204             MaskableOutputData3Low = 0x18,
205             MaskableOutputData3Hi = 0x1C,
206             OutputData0 = 0x40,
207             OutputData1 = 0x44,
208             OutputData2 = 0x48,
209             OutputData3 = 0x4C,
210             InputData0 = 0x60,
211             InputData1 = 0x64,
212             InputData2 = 0x68,
213             InputData3 = 0x6C
214         }
215         private uint[] portOffsets = new uint[] { 0, 32, 54, 86 };
216 
217         /* Common register sets for the all the banks */
218         private class GPIOController
219         {
GPIOController(XilinxGPIOPS parent, uint bankNumber)220             public GPIOController(XilinxGPIOPS parent, uint bankNumber)
221             {
222                 this.parentClass = parent;
223                 this.bankNumber = bankNumber;
224             }
225 
WriteRegister(long offset, uint value)226             public void WriteRegister(long offset, uint value)
227             {
228                 switch((registersOffsets)offset)
229                 {
230                 case registersOffsets.DirectionMode:
231                     DirectionMode = value;
232                     break;
233                 case registersOffsets.OutputEnable:
234                     OutputEnable = value;
235                     break;
236                 default:
237                     this.parentClass.LogUnhandledWrite(0x204 + this.bankNumber * 0x40 + offset, value);
238                     break;
239                 }
240             }
ReadRegister(long offset)241             public uint ReadRegister(long offset)
242             {
243                 switch((registersOffsets)offset)
244                 {
245                 case registersOffsets.DirectionMode:
246                     return DirectionMode;
247                 case registersOffsets.OutputEnable:
248                     return OutputEnable;
249                 default:
250                     this.parentClass.LogUnhandledRead(0x204 + this.bankNumber * 0x40 + offset);
251                     return 0;
252                 }
253             }
OutputEnabled()254             public uint OutputEnabled()
255             {
256                 return (uint)(DirectionMode & OutputEnable);
257             }
258             private XilinxGPIOPS parentClass;
259             private uint bankNumber;
260 
261             /* Registers */
262             private uint DirectionMode = 0x00;
263             private uint OutputEnable = 0x00;
264 
265 
266             private enum registersOffsets : uint
267             {
268                 DirectionMode = 0x04,
269                 OutputEnable = 0x08,
270                 InterruptMaskStatus = 0x0C,
271                 InterruptEnable = 0x10,
272                 InterruptDisable = 0x14,
273                 InterruptPolarity = 0x18,
274                 InterruptAnyEdgeSensitive = 0x1C
275             }
276         }
277     }
278 }
279 
280