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.Core;
10 using Antmicro.Renode.Peripherals.Bus;
11 using Antmicro.Renode.Logging;
12 using System.Collections.Generic;
13 
14 namespace Antmicro.Renode.Peripherals.IRQControllers
15 {
16     public sealed class MSCM : IIRQController, INumberedGPIOOutput, IWordPeripheral, IDoubleWordPeripheral, IKnownSize
17     {
MSCM(IMachine machine)18         public MSCM(IMachine machine)
19         {
20             sysbus = machine.GetSystemBus(this);
21             routingTable = new bool[NumberOfInterrupts * 2];
22             destinations = new Destination[NumberOfInterrupts * 2];
23             interProcessorInterrupts = new GPIO[8];
24             for(var i = 0; i < interProcessorInterrupts.Length; i++)
25             {
26                 interProcessorInterrupts[i] = new GPIO();
27             }
28 
29             Connections = new IGPIORedirector(232, HandleIRQConnect, HandleIRQDisconnect);
30         }
31 
32         public IReadOnlyDictionary<int, IGPIO> Connections { get; private set; }
33 
ReadWord(long offset)34         public ushort ReadWord(long offset)
35         {
36             if(offset >= RoutingControlStart && offset < RoutingControlEnd)
37             {
38                 var interruptNo = (offset - RoutingControlStart) / 2;
39                 lock(routingTable)
40                 {
41                     return (ushort)((routingTable[interruptNo] ? 1u : 0u) + (routingTable[NumberOfInterrupts + interruptNo] ? 2u : 0u));
42                 }
43             }
44             this.LogUnhandledRead(offset);
45             return 0;
46         }
47 
WriteWord(long offset, ushort value)48         public void WriteWord(long offset, ushort value)
49         {
50             if(offset >= RoutingControlStart && offset < RoutingControlEnd)
51             {
52                 var interruptNo = (offset - RoutingControlStart) / 2;
53                 var cpu0 = (value & 1) != 0;
54                 var cpu1 = (value & 2) != 0;
55                 lock(routingTable)
56                 {
57                     this.DebugLog("Interrupt no {0} set to be routed to CPU0: {1}, CPU1: {2}", interruptNo, cpu0, cpu1);
58                     routingTable[interruptNo] = cpu0;
59                     routingTable[NumberOfInterrupts + interruptNo] = cpu1;
60                 }
61             }
62             else
63             {
64                 this.LogUnhandledWrite(offset, value);
65             }
66         }
67 
ReadDoubleWord(long offset)68         public uint ReadDoubleWord(long offset)
69         {
70             switch((Register)offset)
71             {
72             case Register.CP0:
73                 return HandleCPRead(0);
74             case Register.CP1:
75                 return HandleCPRead(1);
76             }
77             this.LogUnhandledRead(offset);
78             return 0;
79         }
80 
WriteDoubleWord(long offset, uint value)81         public void WriteDoubleWord(long offset, uint value)
82         {
83             switch((Register)offset)
84             {
85             case Register.CP0:
86                 HandleCPWrite(0, value);
87                 break;
88             case Register.CP1:
89                 HandleCPWrite(1, value);
90                 break;
91             case Register.GenerateInterrupt:
92                 HandleGenerateInterrupt(value);
93                 break;
94             }
95             this.LogUnhandledWrite(offset, value);
96         }
97 
98         public long Size
99         {
100             get
101             {
102                 return 0x1000;
103             }
104         }
105 
HandleIRQConnect(int sourceNumber, IGPIOReceiver destination, int destinationNumber)106         private void HandleIRQConnect(int sourceNumber, IGPIOReceiver destination, int destinationNumber)
107         {
108             if(sourceNumber > 223)
109             {
110                 lock(interProcessorInterrupts)
111                 {
112                     if(sourceNumber - 224 >= interProcessorInterrupts.Length)
113                     {
114                         throw new ArgumentException("Invalid source number.");
115                     }
116                     interProcessorInterrupts[sourceNumber - 224].Connect(destination, destinationNumber);
117                 }
118                 return;
119             }
120             lock(routingTable)
121             {
122                 destinations[sourceNumber] = new Destination(destination, destinationNumber);
123             }
124         }
125 
HandleIRQDisconnect(int sourceNumber)126         private void HandleIRQDisconnect(int sourceNumber)
127         {
128             lock(routingTable)
129             {
130                 destinations[sourceNumber] = new Destination();
131             }
132         }
133 
OnGPIO(int number, bool value)134         public void OnGPIO(int number, bool value)
135         {
136             lock(routingTable)
137             {
138                 if(routingTable[number])
139                 {
140                     destinations[number].OnGPIO(value);
141                 }
142             }
143         }
144 
Reset()145         public void Reset()
146         {
147             Array.Clear(routingTable, 0, routingTable.Length);
148         }
149 
HandleGenerateInterrupt(uint value)150         private void HandleGenerateInterrupt(uint value)
151         {
152             var interruptId = (int)value & 3;
153             var targetListField = (value >> 24) & 3;
154             var cpuTargetList = (uint)((value >> 16) & 3);
155             if(!sysbus.TryGetCurrentCPU(out var askingCpu))
156             {
157                 this.Log(LogLevel.Warning, "Generate interrupt write by not a CPU - ignoring.");
158                 return;
159             }
160             switch(targetListField)
161             {
162             case 1:
163                 cpuTargetList = 2 - askingCpu.MultiprocessingId;
164                 break;
165             case 2:
166                 cpuTargetList = askingCpu.MultiprocessingId + 1;
167                 break;
168             }
169             lock(interProcessorInterrupts)
170             {
171                 if((cpuTargetList & 1) != 0)
172                 {
173                     interProcessorInterrupts[interruptId].Set();
174                 }
175                 if((cpuTargetList & 2) != 0)
176                 {
177                     interProcessorInterrupts[4 + interruptId].Set();
178                 }
179             }
180         }
181 
HandleCPRead(int cpuNumber)182         private uint HandleCPRead(int cpuNumber)
183         {
184             lock(interProcessorInterrupts)
185             {
186                 var returnValue = 0u;
187                 for(var i = 0; i < 4; i++)
188                 {
189                     returnValue |= interProcessorInterrupts[4 * cpuNumber + i].IsSet ? 1u : 0u;
190                     returnValue <<= 1;
191                 }
192                 returnValue >>= 1;
193                 return returnValue;
194             }
195         }
196 
HandleCPWrite(int cpuNumber, uint value)197         private void HandleCPWrite(int cpuNumber, uint value)
198         {
199             lock(interProcessorInterrupts)
200             {
201                 for(var i = 0; i < 4; i++)
202                 {
203                     if((value & 1) != 0)
204                     {
205                         interProcessorInterrupts[4 * cpuNumber + i].Unset();
206                     }
207                     value >>= 1;
208                 }
209             }
210         }
211 
212         private Destination[] destinations;
213         private readonly bool[] routingTable;
214         private readonly GPIO[] interProcessorInterrupts;
215         private readonly IBusController sysbus;
216 
217         private const int NumberOfInterrupts = 112;
218         private const int RoutingControlStart = 0x880;
219         private const int RoutingControlEnd = RoutingControlStart + NumberOfInterrupts*2;
220 
221         private enum Register
222         {
223             CP0 = 0x800,
224             CP1 = 0x804,
225             GenerateInterrupt = 0x820
226         }
227 
228         private struct Destination
229         {
DestinationAntmicro.Renode.Peripherals.IRQControllers.MSCM.Destination230             public Destination(IGPIOReceiver receiver, int destinationNo)
231             {
232                 this.receiver = receiver;
233                 this.destinationNo = destinationNo;
234             }
235 
OnGPIOAntmicro.Renode.Peripherals.IRQControllers.MSCM.Destination236             public void OnGPIO(bool state)
237             {
238                 receiver.OnGPIO(destinationNo, state);
239             }
240 
241             public readonly IGPIOReceiver receiver;
242             public readonly int destinationNo;
243         }
244     }
245 }
246 
247