1 //
2 // Copyright (c) 2010-2024 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.Logging;
11 using Antmicro.Renode.Peripherals.Bus;
12 using Antmicro.Renode.Utilities;
13 using System.Collections.Generic;
14 using System.Linq;
15 
16 namespace Antmicro.Renode.Peripherals.IRQControllers
17 {
18     public class GaislerMIC: IDoubleWordPeripheral, INumberedGPIOOutput, IIRQController, IGaislerAPB
19     {
GaislerMIC(IMachine machine, uint totalNumberCPUs = 1)20         public GaislerMIC(IMachine machine, uint totalNumberCPUs = 1)
21         {
22             this.numberOfProcessors = totalNumberCPUs;
23             if(totalNumberCPUs > maxNumberOfProcessors)
24             {
25                 this.Log(LogLevel.Warning, "Registration with unsupported  number of CPUs, defaulting to maximum {0:X]", maxNumberOfProcessors);
26                 this.numberOfProcessors = maxNumberOfProcessors;
27             }
28             registers = new deviceRegisters();
29             registers.MultiprocessorStatus |= (((numberOfProcessors-1) << 28) & 0xF0000000);
30             // Set Broadcast Available bit in MultiprocessorStatus register if ncpu > 1
31             if(this.numberOfProcessors > 1)
32             {
33                 registers.MultiprocessorStatus |= (1u << 27);
34             }
35             irqs = new GPIO[numberOfProcessors];
36             resets = new GPIO[numberOfProcessors];
37             runs = new GPIO[numberOfProcessors];
38             set_nmi_interrupt = new bool[numberOfProcessors];
39             for(var i = 0; i < numberOfProcessors; i++)
40             {
41                 irqs[i] = new GPIO();
42                 resets[i] = new GPIO();
43                 runs[i] = new GPIO();
44                 interrupts[i] = new Dictionary<int, int>();
45                 set_nmi_interrupt[i] = false;
46             }
47 
48             Connections = new IGPIORedirector((int)numberOfProcessors, HandleIRQConnect);
49             Reset();
50         }
51 
52         public IReadOnlyDictionary<int, IGPIO> Connections { get; private set; }
53 
HandleIRQConnect(int src, IGPIOReceiver receiver, int dst)54         private void HandleIRQConnect(int src, IGPIOReceiver receiver, int dst)
55         {
56             switch(dst)
57             {
58             case 0:
59                 irqs[src].Connect(receiver, dst);
60                 break;
61             case 1:
62                 resets[src].Connect(receiver, dst);
63                 break;
64             case 2:
65                 runs[src].Connect(receiver, dst);
66                 break;
67             default:
68                 this.Log(LogLevel.Warning, "Destination index value is undefined {0:X}", dst);
69                 break;
70             }
71         }
72 
73         #region IDoubleWordPeripheral implementation
ReadDoubleWord(long offset)74         public uint ReadDoubleWord (long offset)
75         {
76             if(offset < (int)(registerOffset.ProcessorInterruptMaskBase))
77             {
78                 switch((registerOffset)offset)
79                 {
80                 case registerOffset.InterruptLevel:
81                     return registers.InterruptLevel;
82                 case registerOffset.InterruptPending:
83                     return registers.InterruptPending;
84                 case registerOffset.InterruptForce:
85                     return 0;
86                 case registerOffset.InterruptClear:
87                     return 0;
88                 case registerOffset.MultiprocessorStatus:
89                     return registers.MultiprocessorStatus;
90                 case registerOffset.Broadcast:
91                     if(isBroadcastEnabled())
92                     {
93                         return registers.Broadcast;
94                     }
95                     else
96                     {
97                         this.LogUnhandledRead(offset);
98                         return 0;
99                     }
100                 default:
101                     this.LogUnhandledRead(offset);
102                     return 0;
103                 }
104             }
105             else if(offset < (int)(registerOffset.ProcessorInterruptForceBase))
106             {
107                 for(var i = 0; i < numberOfProcessors; i++)
108                 {
109                     if(offset == (int)(registerOffset.ProcessorInterruptMaskBase) + 4 * i)
110                     {
111                         return registers.ProcessorInterruptMask[i];
112                     }
113                 }
114                 this.LogUnhandledRead(offset);
115                 return 0;
116             }
117             else if(offset < (int)(registerOffset.ProcessorExtendedInterruptAcknowledgeBase))
118             {
119                 for(var i = 0; i < numberOfProcessors; i++)
120                 {
121                     if(offset == (int)(registerOffset.ProcessorInterruptForceBase) + 4 * i)
122                     {
123                         return registers.ProcessorInterruptForce[i];
124                     }
125                 }
126                 this.LogUnhandledRead(offset);
127                 return 0;
128             }
129             else if(offset < (int)(registerOffset.ProcessorExtendedInterruptAcknowledgeBase) + 4 * maxNumberOfProcessors)
130             {
131                 for(var i = 0; i < numberOfProcessors; i++)
132                 {
133                     if(offset == (int)(registerOffset.ProcessorExtendedInterruptAcknowledgeBase) + 4 * i)
134                     {
135                         return registers.ProcessorExtendedInterruptAcknowledge[i];
136                     }
137                 }
138                 this.LogUnhandledRead(offset);
139                 return 0;
140             }
141             else
142             {
143                 this.LogUnhandledRead(offset);
144                 return 0;
145             }
146         }
147 
WriteDoubleWord(long offset, uint value)148         public void WriteDoubleWord (long offset, uint value)
149         {
150             if(offset < (int)(registerOffset.ProcessorInterruptMaskBase))
151             {
152                 switch((registerOffset)offset)
153                 {
154                 case registerOffset.InterruptLevel:
155                     // Each interrupt can be assigned to one of two levels (0 or 1) as programmed in
156                     // the interrupt level register - bit 1-15. Level 1 has higher priority than level 0.
157                     if(value < 0xFFFF)
158                     {
159                         registers.InterruptLevel = value;
160                     }
161                     else
162                     {
163                         this.Log(LogLevel.Warning, "Write of unsupported interrupt level value {0:X}", value);
164                     }
165                     break;
166                 case registerOffset.InterruptPending:
167                     // read-only register
168                     this.Log(LogLevel.Warning, "Write to read-only register (InterruptPending) value {0:X}", value);
169                     break;
170                 case registerOffset.InterruptForce:
171                     if(currentNumberCpus() == 1)
172                     {
173                         registers.InterruptPending |= (value & registers.ProcessorInterruptMask[0]);
174                     }
175                     else
176                     {
177                         this.LogUnhandledWrite(offset, value);
178                     }
179                     break;
180                 case registerOffset.InterruptClear:
181                     // Don't clear interrupts whose input is set
182                     registers.InterruptPending &= ~(value & ~pirqState);
183                     break;
184                 case registerOffset.MultiprocessorStatus:
185                     // A halted processor can be reset and restarted by writing a ‘1’ to its status field. Bit field = [15:0]
186                     if((value & 0xF) != 0)
187                     {
188                         for(var i = 0; i < numberOfProcessors; i++)
189                         {
190                             // Check if CPU is halted and then if it is requested to reset
191                             if((( ~(registers.MultiprocessorStatus >> i) & 0x1) == 0x1)
192                                 && (((value >> i) & 0x1) == 0x1))
193                             {
194                                     resets[i].Set();
195                                     runs[i].Set();
196                             }
197                         }
198                     }
199                     // Make setting a bit sticky
200                     registers.MultiprocessorStatus |= value;
201                     break;
202                 case registerOffset.Broadcast:
203                     if(isBroadcastEnabled())
204                     {
205                         registers.Broadcast = value;
206                     }
207                     break;
208                 default:
209                     this.LogUnhandledWrite(offset, value);
210                     break;
211                 }
212             }
213             else if(offset < (int)(registerOffset.ProcessorInterruptForceBase))
214             {
215                 for(var i = 0; i < numberOfProcessors; i++)
216                 {
217                     // TODO: Interrupt 15 cannot be masked, should be handled here
218                     if(offset == (int)(registerOffset.ProcessorInterruptMaskBase) + 4 * i)
219                     {
220                         registers.ProcessorInterruptMask[i] = value;
221                     }
222                 }
223             }
224             else if(offset < (int)(registerOffset.ProcessorExtendedInterruptAcknowledgeBase))
225             {
226                 int cpuid = (int)(offset - (int)(registerOffset.ProcessorInterruptForceBase))/4;
227 
228                 // Loop over the external interrupts in 'value' and if set insert them into the
229                 // cpu's interrupt force register and the cpu's pending interrupt list. Extended
230                 // interrupts (see VHDL generic eirq in the GRLIB IP Core Manual) are not dealt
231                 // with at the moment.
232                 for (int interrupt = 1; interrupt < maxNumberOfExternalInterrupts; interrupt++)
233                 {
234                     uint interrupt_mask = (1u << interrupt);
235                     if ((value & interrupt_mask) != 0x0) {
236                         if((interrupt_mask & registers.ProcessorInterruptMask[cpuid]) != 0)
237                         {
238                             lock(interrupts[cpuid])
239                             {
240                                 registers.ProcessorInterruptForce[cpuid] |= interrupt_mask;
241                                 addPendingInterrupt(cpuid, interrupt);
242                                 if (interrupt == NMI_IRQ)
243                                 {
244                                     set_nmi_interrupt[cpuid] = true;
245                                 }
246                             }
247                         }
248                     }
249                 }
250             }
251             else if(offset < (int)(registerOffset.ProcessorExtendedInterruptAcknowledgeBase) + 4 * maxNumberOfProcessors)
252             {
253                 this.Log(LogLevel.Warning, "Write to read-only register (ProcessorExtendedInterruptAcknowledge) value {0:X}", value);
254             }
255             else
256             {
257                 this.LogUnhandledWrite(offset, value);
258             }
259             this.forwardInterrupt();
260         }
261         #endregion
262 
263         #region IPeripheral implementation
Reset()264         public void Reset ()
265         {
266             for(var i = 0; i < numberOfProcessors; i++)
267             {
268                 registers.ProcessorInterruptMask[i] = 0;
269                 registers.ProcessorInterruptForce[i] = 0;
270             }
271             pirqState = 0;
272         }
273         #endregion
274 
275         #region IGPIOReceiver implementation
OnGPIO(int number, bool value)276         public void OnGPIO(int number, bool value)
277         {
278             int i;
279             uint pendingInterrupts = 0;
280             uint processorPendingInterrupts = 0;
281 
282             if(value)
283             {
284                 pirqState |= (1u << number);
285                 pendingInterrupts |= (1u << number);
286                 // If interrupt is enabled in Broadcast register use cpu Force registers instead of global Pending
287                 if(isBroadcastEnabled() && ((registers.Broadcast & pendingInterrupts) != 0))
288                 {
289                     for(i = 0; i < numberOfProcessors; i++)
290                     {
291                         processorPendingInterrupts = pendingInterrupts & registers.ProcessorInterruptMask[i];
292                         if(processorPendingInterrupts != 0)
293                         {
294                             lock(interrupts[i])
295                             {
296                                 registers.ProcessorInterruptForce[i] |= processorPendingInterrupts;
297                                 addPendingInterrupt(i, number);
298                                 if (number == NMI_IRQ)
299                                 {
300                                     set_nmi_interrupt[i] = true;
301                                 }
302                             }
303                         }
304                     }
305                 }
306                 else
307                 {
308                     for(i = 0; i < numberOfProcessors; i++)
309                     {
310                         processorPendingInterrupts = pendingInterrupts & registers.ProcessorInterruptMask[i];
311                         if(processorPendingInterrupts != 0)
312                         {
313                             lock(interrupts[i])
314                             {
315                                 registers.InterruptPending |= processorPendingInterrupts;
316                                 addPendingInterrupt(i, number);
317                                 if (number == NMI_IRQ)
318                                 {
319                                     set_nmi_interrupt[i] = true;
320                                 }
321                             }
322                         }
323                     }
324                 }
325             }
326             else
327             {
328                 pirqState &= ~(1u << number);
329             }
330 
331             this.forwardInterrupt();
332         }
333         #endregion
334 
335         #region IGaislerAPB implementation
GetVendorID()336         public uint GetVendorID ()
337         {
338             return vendorID;
339         }
340 
GetDeviceID()341         public uint GetDeviceID ()
342         {
343             return deviceID;
344         }
345 
GetSpaceType()346         public GaislerAPBPlugAndPlayRecord.SpaceType GetSpaceType ()
347         {
348             return spaceType;
349         }
350 
GetInterruptNumber()351         public uint GetInterruptNumber()
352         {
353             return this.GetCpuInterruptNumber(irqs[0]);
354         }
355         #endregion
356 
GetNumberOfProcessors()357         public uint GetNumberOfProcessors()
358         {
359             return this.numberOfProcessors;
360         }
361 
GetCurrentCpuIrq(int index)362         public GPIO GetCurrentCpuIrq (int index)
363         {
364             GPIO currentCpuIrq = null;
365             if(index < numberOfProcessors)
366             {
367                 currentCpuIrq = irqs[index];
368             }
369             else
370             {
371                 this.NoisyLog("Current IRQ array index is out of range {0:X}.", index);
372             }
373             return currentCpuIrq;
374         }
375 
forwardInterrupt()376         private void forwardInterrupt()
377         {
378             for(var i = 0; i < numberOfProcessors; i++)
379             {
380                 lock(interrupts[i])
381                 {
382                     // If broadcast is set for an irq send this to each CPU
383                     if(isBroadcastEnabled())
384                     {
385                         if((!irqs[i].IsSet) && (registers.ProcessorInterruptForce[i] != 0))
386                         {
387                             irqs[i].Set();
388                         }
389                     }
390                     if((!irqs[i].IsSet) && (registers.InterruptPending & registers.ProcessorInterruptMask[i]) != 0)
391                     {
392                         irqs[i].Set();
393                     }
394 
395                     // Always forward the NMI interrupt, even if the cpu is already servicing
396                     // another interrupt. Not doing this when running an SMP Linux kernel will
397                     // result in a deadlock in the kernels cpu cross call mechanism.
398                     if(set_nmi_interrupt[i])
399                     {
400                         irqs[i].Unset();
401                         irqs[i].Set();
402                         set_nmi_interrupt[i] = false;
403                     }
404                 }
405             }
406         }
407 
408         // Needs to be (interrupts[i]) locked from caller
forwardInterruptSingleCPU(int cpuid)409         private void forwardInterruptSingleCPU(int cpuid)
410         {
411             // If broadcast is set for an irq send this to each CPU
412             if(isBroadcastEnabled())
413             {
414                 if((!irqs[cpuid].IsSet) && (registers.ProcessorInterruptForce[cpuid] != 0))
415                 {
416                     irqs[cpuid].Set();
417                 }
418             }
419             if((!irqs[cpuid].IsSet) && (registers.InterruptPending & registers.ProcessorInterruptMask[cpuid]) != 0)
420             {
421                 irqs[cpuid].Set();
422             }
423         }
424 
CPUGetInterrupt(int cpuid)425         public int CPUGetInterrupt(int cpuid)
426         {
427             lock(interrupts[cpuid])
428             {
429                 if(interrupts[cpuid].Any())
430                 {
431                     // Find interrupt with highest priority for this CPU
432                     var interrupt = interrupts[cpuid].OrderByDescending(x => x.Value).First().Key;
433                     // Treat all extended interrupts as interrupt #1
434                     if(IsExtendedInterruptNumber(interrupt))
435                     {
436                         interrupt = 1;
437                     }
438                     // As the irq no is external, we have to add 0x10
439                     var intNo = interrupt + 0x10;
440                     return intNo;
441                 }
442             }
443             return 0;
444         }
445 
446         // When a processor acknowledges the interrupt, the corresponding pending bit will automatically be
447         // cleared. Interrupt can also be forced by setting a bit in the interrupt force register.
448         // In this case, the processor acknowledgement will clear the force bit rather than the pending bit.
CPUAckInterrupt(int cpuid, int interruptNumber)449         public void CPUAckInterrupt(int cpuid, int interruptNumber)
450         {
451             // Have to subtract 0x10 as the irq is external
452             var realInterruptNumber = interruptNumber - 0x10;
453 
454             lock(interrupts[cpuid])
455             {
456                 // Handle extended interrupts, which are all mapped to IRQ 1
457                 if(realInterruptNumber == 1)
458                 {
459                     // Extended interrupts have no prioritization between individual interrupts
460                     var extendedInt = interrupts[cpuid].Keys.FirstOrDefault(IsExtendedInterruptNumber);
461                     if(extendedInt == 0)
462                     {
463                         // This is unusual, but supported by the documentation
464                         this.DebugLog("Interrupt #1 acknowledged without a pending extended interrupt.");
465                         registers.ProcessorExtendedInterruptAcknowledge[cpuid] = 0; // Clear PEXTACK register
466                     }
467                     else
468                     {
469                         realInterruptNumber = extendedInt;
470                         registers.ProcessorExtendedInterruptAcknowledge[cpuid] = (uint)extendedInt;
471                     }
472                 }
473 
474                 // Check the irq is forced
475                 var interruptMask = 1u << realInterruptNumber;
476                 if((registers.ProcessorInterruptForce[cpuid] & interruptMask) != 0)
477                 {
478                     registers.ProcessorInterruptForce[cpuid] &= ~interruptMask;
479                     interrupts[cpuid].Remove(realInterruptNumber);
480                     if(irqs[cpuid].IsSet)
481                     {
482                         irqs[cpuid].Unset();
483                     }
484                 }
485                 else
486                 {
487                     // Check if the interrupt is still pending, needs an ACK and the input is not set
488                     if((registers.InterruptPending & interruptMask) != 0 && (pirqState & interruptMask) == 0)
489                     {
490                         // Remove the global pending interrupt
491                         registers.InterruptPending &= ~interruptMask;
492                         interrupts[cpuid].Remove(realInterruptNumber);
493                         if(irqs[cpuid].IsSet)
494                         {
495                             irqs[cpuid].Unset();
496                         }
497                     }
498                 }
499                 this.forwardInterruptSingleCPU(cpuid);
500             }
501         }
502 
addPendingInterrupt(int cpuid, int number)503         private void addPendingInterrupt(int cpuid, int number)
504         {
505             // Interrupts are added per CPU and have been checked against processor irq mask
506             // in OnGPIO function before call - only handle priority here
507             // Interrupt Level is either high (1) or low (0) - irq is prioritized per level, with 15 as highest
508             // Extended interrupts have no prioritization between individual interrupts
509 
510             int interruptPriority;
511             if(IsExtendedInterruptNumber(number))
512             {
513                 interruptPriority = 1;
514             }
515             else
516             {
517                 interruptPriority = number + (((registers.InterruptLevel & 1u<<number) != 0) ? 16 : 0);
518             }
519 
520             // Safe as this is only called under the interrupts[cpuid] lock
521             if(!interrupts[cpuid].ContainsKey(number))
522             {
523                 interrupts[cpuid].Add(number, interruptPriority);
524             }
525         }
526 
currentNumberCpus()527         private int currentNumberCpus ()
528         {
529             return (int)((registers.MultiprocessorStatus >> 28) & 0xF);
530         }
531 
isBroadcastEnabled()532         private bool isBroadcastEnabled ()
533         {
534             if(((registers.MultiprocessorStatus >> 27) & 0x1) == 0x1)
535             {
536                 return true;
537             }
538             else
539             {
540                 return false;
541             }
542         }
543 
IsExtendedInterruptNumber(int irq)544         private static bool IsExtendedInterruptNumber(int irq)
545         {
546             return irq >= 16 && irq <= 32;
547         }
548 
549         private readonly bool[] set_nmi_interrupt;
550         private readonly uint numberOfProcessors;
551         private readonly uint vendorID = 0x01;  // Aeroflex Gaisler
552         private readonly uint deviceID = 0x00d; // GRLIB IRQMP
553         private static uint maxNumberOfProcessors = 16;
554         private readonly GaislerAPBPlugAndPlayRecord.SpaceType spaceType = GaislerAPBPlugAndPlayRecord.SpaceType.APBIOSpace;
555         private deviceRegisters registers;
556         private uint pirqState;
557         private readonly GPIO[] irqs;
558         private readonly GPIO[] resets;
559         private readonly GPIO[] runs;
560         private Dictionary<int, int>[] interrupts = new Dictionary<int, int>[maxNumberOfProcessors];
561 
562         private enum registerOffset : uint
563         {
564             InterruptLevel = 0x00,
565             InterruptPending = 0x04,
566             InterruptForce = 0x08,
567             InterruptClear = 0x0C,
568             MultiprocessorStatus = 0x10,
569             Broadcast = 0x14,
570             ProcessorInterruptMaskBase = 0x40,
571             ProcessorInterruptForceBase = 0x80,
572             ProcessorExtendedInterruptAcknowledgeBase = 0xC0
573         }
574 
575         private class deviceRegisters
576         {
577             public uint InterruptLevel;
578             public uint InterruptPending;
579             public uint MultiprocessorStatus = 0x01 | (1 << 16); // 1 - interrupt number used for extended IRQs
580             public uint Broadcast;
581             public uint[] ProcessorInterruptMask;
582             public uint[] ProcessorInterruptForce;
583             public uint[] ProcessorExtendedInterruptAcknowledge;
584 
deviceRegisters()585             public deviceRegisters()
586             {
587                 ProcessorInterruptMask = new uint[maxNumberOfProcessors];
588                 ProcessorInterruptForce = new uint[maxNumberOfProcessors];
589                 ProcessorExtendedInterruptAcknowledge = new uint[maxNumberOfProcessors];
590             }
591         }
592 
593         public const int maxNumberOfExternalInterrupts = 16;
594         public const int NMI_IRQ = 15;
595     }
596 }
597 
598