1 //
2 // Copyright (c) 2010-2024 Antmicro
3 // Copyright (c) 2021 Google LLC
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 System.Collections.Generic;
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.Peripherals.IRQControllers.PLIC;
16 
17 namespace Antmicro.Renode.Peripherals.IRQControllers
18 {
19     [AllowedTranslations(AllowedTranslation.ByteToDoubleWord)]
20     public class OpenTitan_PlatformLevelInterruptController : PlatformLevelInterruptControllerBase, IKnownSize
21     {
OpenTitan_PlatformLevelInterruptController(int numberOfSources = 84, int numberOfContexts = 1)22         public OpenTitan_PlatformLevelInterruptController(int numberOfSources = 84, int numberOfContexts = 1)
23             // we set numberOfSources + 1, because ID 0 is reserved and represents no interrupt
24             : base(numberOfSources + 1, numberOfContexts, prioritiesEnabled: true, extraInterrupts: (uint)numberOfContexts)
25         {
26             // OpenTitan PLIC implementation contains an additional register per hart.
27             // Each of these memory mapped registers is intended to be connected to the MSIP bits
28             // within the MIP CSR registers of the individial harts.
29             // This allows interprocessor interrupts between harts.
30 
31             FatalAlert = new GPIO();
32 
33             // OpenTitan PLIC implementation source is limited
34             if(numberOfSources + 1 > MaxNumberOfSources)
35             {
36                 throw new ConstructionException($"Current {this.GetType().Name} implementation does not support more than {MaxNumberOfSources} sources");
37             }
38 
39             if(numberOfContexts > MaxNumberOfContexts)
40             {
41                 throw new ConstructionException($"Current {this.GetType().Name} implementation does not support more than {MaxNumberOfContexts} contexts");
42             }
43 
44             this.numberOfContexts = numberOfContexts;
45 
46             var registersMap = new Dictionary<long, DoubleWordRegister>();
47 
48             var interruptPendingRegisterCount = (int)Math.Ceiling((numberOfSources + 1) / 32.0);
49             for(var i = 0; i < interruptPendingRegisterCount; i++)
50             {
51                 var interruptPendingOffset = (long)Registers.InterruptPending0 + i * 4;
52                 this.Log(LogLevel.Noisy, "Creating InterruptPending{0}[0x{1:X}]", i, interruptPendingOffset);
53                 registersMap.Add(interruptPendingOffset, new DoubleWordRegister(this)
54                     .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ =>
55                         {
56                             // TODO: Build register value from pending sources IRQs
57                             return 0;
58                         }));
59             }
60 
61             for(uint i = 0; i <= numberOfSources; i++)
62             {
63                 var j = i;
64                 var sourcePriorityOffset = (uint)Registers.InterruptPriority0 + (4u * i);
65                 this.Log(LogLevel.Noisy, "Creating SourcePriority{0}[0x{1:X}]", i, sourcePriorityOffset);
66                 registersMap.Add(sourcePriorityOffset, new DoubleWordRegister(this)
67                     .WithValueField(0, 3,
68                                     valueProviderCallback: _ => irqSources[j].Priority,
69                                     writeCallback: (_, value) =>
70                                     {
71                                         irqSources[j].Priority = (uint)value;
72                                         RefreshInterrupts();
73                                     })
74                      .WithReservedBits(3, 29));
75             }
76 
77             for(uint i = 0; i < numberOfContexts; i++)
78             {
79                 var contextMachineEnablesOffset = (long)Registers.InterruptEnable0 + (i * ContextRegistersOffset);
80                 this.Log(LogLevel.Noisy, $"ContextMachineEnablesOffset for Context{i} 0x{contextMachineEnablesOffset:X}");
81                 AddContextEnablesRegister(registersMap, contextMachineEnablesOffset, i, numberOfSources);
82 
83                 var contextPriorityThresholdOffset = (uint) Registers.Target0Threshold + (i * GeneralContextRegistersOffset);
84                 AddContextPriorityThresholdRegister(registersMap, contextPriorityThresholdOffset, i);
85 
86                 var contextClaimCompleteOffset = contextPriorityThresholdOffset + 0x4;
87                 AddContextClaimCompleteRegister(registersMap, contextClaimCompleteOffset, i);
88 
89                 var contextSoftwareOffset = (uint)Registers.Target0SoftwareInterrupt + (i * 0x4);
90                 AddContextSoftwareInterruptRegister(registersMap, contextSoftwareOffset, i);
91             }
92 
93             registers = new DoubleWordRegisterCollection(this, registersMap);
94 
95             Registers.AlertTestRegister.Define(registers)
96                 .WithFlag(0, FieldMode.Write, writeCallback: (_, val) => { if(val) FatalAlert.Blink(); }, name: "fatal_fault") // FieldMode.Write
97                 .WithIgnoredBits(1, 31);
98         }
99 
100         public long Size => 0x8000000;
101 
102         public GPIO FatalAlert { get; }
103 
IsIrqSourceAvailable(int number)104         protected override bool IsIrqSourceAvailable(int number)
105         {
106             // source with ID 0 is reserved and represents no interrupt
107             return number != 0 && base.IsIrqSourceAvailable(number);
108         }
109 
AddContextSoftwareInterruptRegister(Dictionary<long, DoubleWordRegister> registersMap, long offset, uint hartId)110         protected void AddContextSoftwareInterruptRegister(Dictionary<long, DoubleWordRegister> registersMap, long offset, uint hartId)
111         {
112             this.Log(LogLevel.Noisy, "Adding Context {0} software interrupt register address 0x{1:X}", hartId, offset);
113             registersMap.Add(offset, new DoubleWordRegister(this)
114                 .WithFlag(0,
115                     valueProviderCallback: _ =>
116                     {
117                         return Connections[Connections.Count - numberOfContexts + (int)hartId].IsSet;
118                     },
119                     writeCallback: (_, value) =>
120                     {
121                         this.Log(LogLevel.Noisy, "Setting software interrupt for Context {0} to {1}", hartId, value);
122                         Connections[Connections.Count - numberOfContexts + (int)hartId].Set(value);
123                     })
124                 .WithReservedBits(1, 31));
125         }
126 
127         private readonly int numberOfContexts;
128 
129         private const int MaxNumberOfSources = 255;
130 
131         private const int MaxNumberOfContexts = ((int)Registers.InterruptEnable0 - (int)Registers.InterruptPending0) / ContextRegistersOffset;
132 
133         private const int ContextRegistersOffset = 0x100;
134 
135         private const int GeneralContextRegistersOffset = 0x1000;
136 
137 #pragma warning disable format
138         private enum Registers : long
139         {
140             InterruptPriority0       = 0x0,
141             InterruptPending0        = 0x1000,
142             InterruptEnable0         = 0x2000,
143             Target0Threshold         = 0x200000,
144             Target0InterruptClaim    = 0x200004,
145             Target0SoftwareInterrupt = 0x4000000,
146             AlertTestRegister        = 0x4004000,
147         }
148 #pragma warning restore format
149     }
150 }
151