1 //
2 // Copyright (c) 2010-2024 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.Exceptions;
13 using Antmicro.Renode.Logging;
14 using Antmicro.Renode.Peripherals.Bus;
15 using Antmicro.Renode.Utilities;
16 using Antmicro.Renode.Peripherals.CPU;
17 using Antmicro.Renode.Peripherals.IRQControllers.PLIC;
18 
19 namespace Antmicro.Renode.Peripherals.IRQControllers
20 {
21     [AllowedTranslations(AllowedTranslation.QuadWordToDoubleWord | AllowedTranslation.ByteToDoubleWord)]
22     public class PlatformLevelInterruptController : PlatformLevelInterruptControllerBase, IKnownSize
23     {
PlatformLevelInterruptController(int numberOfSources, int numberOfContexts, bool prioritiesEnabled = true)24         public PlatformLevelInterruptController(int numberOfSources, int numberOfContexts, bool prioritiesEnabled = true)
25             // we set numberOfSources + 1, because the standard PLIC controller counts sources from 1
26             : base(numberOfSources + 1, numberOfContexts, prioritiesEnabled)
27         {
28             if(numberOfSources + 1 > MaxSources)
29             {
30                 throw new ConstructionException($"Current {this.GetType().Name} implementation does not support more than {MaxSources} sources");
31             }
32 
33             var registersMap = new Dictionary<long, DoubleWordRegister>();
34 
35             registersMap.Add((long)Registers.Source0Priority, new DoubleWordRegister(this)
36                 .WithValueField(0, 3, FieldMode.Read, writeCallback: (_, value) =>
37                 {
38                     if(value != 0)
39                     {
40                         this.Log(LogLevel.Warning, $"Trying to set priority {value} for Source 0, which is illegal");
41                     }
42                 }));
43             for(var i = 1; i <= numberOfSources; i++)
44             {
45                 var j = i;
46                 registersMap[(long)Registers.Source1Priority * i] = new DoubleWordRegister(this)
47                     .WithValueField(0, 3,
48                                     valueProviderCallback: (_) => irqSources[j].Priority,
49                                     writeCallback: (_, value) =>
50                                     {
51                                         if(prioritiesEnabled)
52                                         {
53                                             irqSources[j].Priority = (uint)value;
54                                             RefreshInterrupts();
55                                         }
56                                     });
57             }
58 
59             for(var i = 0u; i < numberOfContexts; i++)
60             {
61                 AddContextEnablesRegister(registersMap, (long)Registers.Context0Enables + i * ContextEnablesWidth, i, numberOfSources);
62                 AddContextClaimCompleteRegister(registersMap, (long)Registers.Context0ClaimComplete + i * ContextClaimWidth, i);
63                 AddContextPriorityThresholdRegister(registersMap, (long)Registers.Context0PriorityThreshold + i * ContextClaimWidth, i);
64             }
65 
66             registers = new DoubleWordRegisterCollection(this, registersMap);
67         }
68 
69         public long Size => 0x4000000;
70 
IsIrqSourceAvailable(int number)71         protected override bool IsIrqSourceAvailable(int number)
72         {
73             // the standard PLIC controller does not support source 0
74             return number != 0 && base.IsIrqSourceAvailable(number);
75         }
76 
77         private enum Registers : long
78         {
79             Source0Priority = 0x0, //this is a fake register, as there is no source 0, but the software writes to it anyway.
80             Source1Priority = 0x4,
81             Source2Priority = 0x8,
82             // ...
83             StartOfPendingArray = 0x1000,
84             Context0Enables = 0x2000,
85             Context1Enables = 0x2080,
86             Context2Enables = 0x2100,
87             // ...
88             Context0PriorityThreshold = 0x200000,
89             Context0ClaimComplete = 0x200004,
90             // ...
91             Context1PriorityThreshold = 0x201000,
92             Context1ClaimComplete = 0x201004,
93             // ...
94             Context2PriorityThreshold = 0x202000,
95             Context2ClaimComplete = 0x202004,
96             //
97             Context3PriorityThreshold = 0x203000,
98             Context3ClaimComplete = 0x203004,
99             //
100             Context4PriorityThreshold = 0x204000,
101             Context4ClaimComplete = 0x204004,
102             // ...
103         }
104 
105         private const long ContextEnablesWidth = Registers.Context1Enables - Registers.Context0Enables;
106         private const long ContextClaimWidth = Registers.Context1ClaimComplete - Registers.Context0ClaimComplete;
107         private const uint MaxSources = (uint)(ContextEnablesWidth / 4) * 32;
108     }
109 }
110