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.PLIC 20 { 21 public abstract class PlatformLevelInterruptControllerBase : IPlatformLevelInterruptController, IDoubleWordPeripheral, IIRQController, INumberedGPIOOutput 22 { PlatformLevelInterruptControllerBase(int numberOfSources, int numberOfContexts, bool prioritiesEnabled, uint extraInterrupts = 0)23 public PlatformLevelInterruptControllerBase(int numberOfSources, int numberOfContexts, bool prioritiesEnabled, uint extraInterrupts = 0) 24 { 25 var connections = new Dictionary<int, IGPIO>(); 26 for(var i = 0; i < numberOfContexts + extraInterrupts; i++) 27 { 28 connections[i] = new GPIO(); 29 } 30 Connections = connections; 31 32 this.prioritiesEnabled = prioritiesEnabled; 33 34 irqSources = new IrqSource[numberOfSources]; 35 for(var i = 0u; i < numberOfSources; i++) 36 { 37 irqSources[i] = new IrqSource(i, this); 38 } 39 40 irqContexts = new IrqContext[numberOfContexts]; 41 for(var i = 0u; i < irqContexts.Length; i++) 42 { 43 irqContexts[i] = new IrqContext(i, this); 44 } 45 } 46 ReadDoubleWord(long offset)47 public uint ReadDoubleWord(long offset) 48 { 49 return registers.Read(offset); 50 } 51 Reset()52 public void Reset() 53 { 54 this.Log(LogLevel.Noisy, "Resetting peripheral state"); 55 56 registers.Reset(); 57 foreach(var irqSource in irqSources) 58 { 59 irqSource.Reset(); 60 } 61 foreach(var irqContext in irqContexts) 62 { 63 irqContext.Reset(); 64 } 65 RefreshInterrupts(); 66 } 67 WriteDoubleWord(long offset, uint value)68 public void WriteDoubleWord(long offset, uint value) 69 { 70 registers.Write(offset, value); 71 } 72 OnGPIO(int number, bool value)73 public void OnGPIO(int number, bool value) 74 { 75 if(!IsIrqSourceAvailable(number)) 76 { 77 this.Log(LogLevel.Error, "Wrong gpio source: {0}", number); 78 return; 79 } 80 lock(irqSources) 81 { 82 this.Log(LogLevel.Noisy, "Setting GPIO number #{0} to value {1}", number, value); 83 var irq = irqSources[number]; 84 irq.State = value; 85 86 if(value) 87 { 88 foreach(var irqContext in irqContexts) 89 { 90 irqContext.MarkSourceAsPending(irq); 91 } 92 } 93 94 RefreshInterrupts(); 95 } 96 } 97 98 public IReadOnlyDictionary<int, IGPIO> Connections { get; } 99 100 /// <summary> 101 /// Setting this property to a value different than -1 causes all interrupts to be reported to a context with a given id. 102 /// 103 /// This is mostly for debugging purposes. 104 /// It allows to designate a single core (in a multi-core setup) to handle external interrupts making it easier to debug trap handlers. 105 /// </summary> 106 public int ForcedContext { get; set; } = -1; 107 RefreshInterrupts()108 protected void RefreshInterrupts() 109 { 110 lock(irqSources) 111 { 112 foreach(var context in irqContexts) 113 { 114 context.RefreshInterrupt(); 115 } 116 } 117 } 118 AddContextClaimCompleteRegister(Dictionary<long, DoubleWordRegister> registersMap, long offset, uint contextId)119 protected void AddContextClaimCompleteRegister(Dictionary<long, DoubleWordRegister> registersMap, long offset, uint contextId) 120 { 121 registersMap.Add(offset, new DoubleWordRegister(this).WithValueField(0, 32, valueProviderCallback: _ => 122 { 123 lock(irqSources) 124 { 125 return irqContexts[contextId].AcknowledgePendingInterrupt(); 126 } 127 }, 128 writeCallback: (_, value) => 129 { 130 if(!IsIrqSourceAvailable((int)value)) 131 { 132 this.Log(LogLevel.Error, "Trying to complete handling of non-existing interrupt source {0}", value); 133 return; 134 } 135 lock(irqSources) 136 { 137 irqContexts[contextId].CompleteHandlingInterrupt(irqSources[value]); 138 } 139 })); 140 } 141 AddContextEnablesRegister(Dictionary<long, DoubleWordRegister> registersMap, long address, uint contextId, int numberOfSources)142 protected void AddContextEnablesRegister(Dictionary<long, DoubleWordRegister> registersMap, long address, uint contextId, int numberOfSources) 143 { 144 var maximumSourceDoubleWords = (int)Math.Ceiling((numberOfSources + 1) / 32.0) * 4; 145 146 for(var offset = 0u; offset < maximumSourceDoubleWords; offset += 4) 147 { 148 var lOffset = offset; 149 registersMap.Add(address + offset, new DoubleWordRegister(this).WithValueField(0, 32, writeCallback: (_, value) => 150 { 151 lock(irqSources) 152 { 153 // Each source is represented by one bit. offset and lOffset indicate the offset in double words from ContextXEnables, 154 // `bit` is the bit number in the given double word, 155 // and `sourceIdBase + bit` indicate the source number. 156 var sourceIdBase = lOffset * 8; 157 var bits = BitHelper.GetBits((uint)value); 158 for(var bit = 0u; bit < bits.Length; bit++) 159 { 160 var sourceNumber = sourceIdBase + bit; 161 if(!IsIrqSourceAvailable((int)sourceNumber)) 162 { 163 if(bits[bit]) 164 { 165 this.Log(LogLevel.Warning, "Trying to enable non-existing source: {0}", sourceNumber); 166 } 167 continue; 168 } 169 170 irqContexts[contextId].EnableSource(irqSources[sourceNumber], bits[bit]); 171 } 172 RefreshInterrupts(); 173 } 174 })); 175 } 176 } 177 AddContextPriorityThresholdRegister(Dictionary<long, DoubleWordRegister> registersMap, long offset, uint hartId)178 protected void AddContextPriorityThresholdRegister(Dictionary<long, DoubleWordRegister> registersMap, long offset, uint hartId) 179 { 180 this.Log(LogLevel.Noisy, "Adding Context {0} threshold priority register address 0x{1:X}", hartId, offset); 181 registersMap.Add(offset, new DoubleWordRegister(this) 182 .WithValueField(0, 32, 183 valueProviderCallback: _ => 184 { 185 // TODO: implement 186 return 0; 187 }, 188 writeCallback: (_, value) => 189 { 190 // TODO: implement 191 // Only log a warning if the software attempts to set a nonzero threshold as the model behaves as if it is 0 192 this.Log(prioritiesEnabled && value != 0 ? LogLevel.Warning : LogLevel.Noisy, "Setting priority threshold for Context {0} not supported", hartId); 193 })); 194 } 195 IsIrqSourceAvailable(int number)196 protected virtual bool IsIrqSourceAvailable(int number) 197 { 198 return number >= 0 && number < irqSources.Length; 199 } 200 201 protected DoubleWordRegisterCollection registers; 202 203 protected readonly bool prioritiesEnabled; 204 205 protected readonly IrqSource[] irqSources; 206 protected readonly IrqContext[] irqContexts; 207 } 208 } 209