1 // 2 // Copyright (c) 2010-2025 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.Collections.Generic; 8 using System.Collections.ObjectModel; 9 using Antmicro.Renode.Core; 10 using Antmicro.Renode.Core.Structure.Registers; 11 using Antmicro.Renode.Exceptions; 12 using Antmicro.Renode.Peripherals.Bus; 13 using Antmicro.Renode.Peripherals.Timers; 14 15 namespace Antmicro.Renode.Peripherals.IRQControllers 16 { 17 [AllowedTranslations(AllowedTranslation.QuadWordToDoubleWord)] 18 public class CoreLevelInterruptor : IDoubleWordPeripheral, IKnownSize, INumberedGPIOOutput, IRiscVTimeProvider 19 { CoreLevelInterruptor(IMachine machine, long frequency, int numberOfTargets = 1, uint divider = 1)20 public CoreLevelInterruptor(IMachine machine, long frequency, int numberOfTargets = 1, uint divider = 1) 21 { 22 this.machine = machine; 23 this.timerFrequency = frequency; 24 if(numberOfTargets < 1) 25 { 26 throw new ConstructionException("Invalid numberOfTargets: provided {numberOfTargets} but should be greater or equal to 1."); 27 } 28 for(var i = 0; i < numberOfTargets; i++) 29 { 30 var hartId = i; 31 irqs[2 * hartId] = new GPIO(); 32 irqs[2 * hartId + 1] = new GPIO(); 33 34 var timer = new ComparingTimer(machine.ClockSource, timerFrequency, this, hartId.ToString(), enabled: true, eventEnabled: true, divider: divider); 35 timer.CompareReached += () => irqs[2 * hartId + 1].Set(true); 36 37 mTimers.Add(timer); 38 } 39 40 var registersMap = new Dictionary<long, DoubleWordRegister> 41 { 42 { 43 (long)Registers.MTimeLo, new DoubleWordRegister(this).WithValueField(0, 32, FieldMode.Read, 44 valueProviderCallback: _ => (uint)TimerValue, 45 writeCallback: (_, value) => 46 { 47 var timerValue = TimerValue; 48 timerValue &= ~0xffffffffUL; 49 timerValue |= value; 50 foreach(var timer in mTimers) 51 { 52 timer.Value = timerValue; 53 } 54 55 }) 56 }, 57 { 58 (long)Registers.MTimeHi, new DoubleWordRegister(this).WithValueField(0, 32, FieldMode.Read, 59 valueProviderCallback: _ => (uint)(TimerValue >> 32), 60 writeCallback: (_, value) => 61 { 62 var timerValue = TimerValue; 63 timerValue &= 0xffffffffUL; 64 timerValue |= (ulong)value << 32; 65 foreach(var timer in mTimers) 66 { 67 timer.Value = timerValue; 68 } 69 }) 70 } 71 }; 72 73 for(var hart = 0; hart < numberOfTargets; ++hart) 74 { 75 var hartId = hart; 76 registersMap.Add((long)Registers.MSipHart0 + 4 * hartId, new DoubleWordRegister(this).WithFlag(0, writeCallback: (_, value) => { irqs[2 * hartId].Set(value); })); 77 registersMap.Add((long)Registers.MTimeCmpHart0Lo + 8 * hartId, new DoubleWordRegister(this).WithValueField(0, 32, writeCallback: (_, value) => 78 { 79 var limit = mTimers[hartId].Compare; 80 limit &= ~0xffffffffUL; 81 limit |= value; 82 83 irqs[2 * hartId + 1].Set(false); 84 mTimers[hartId].Compare = limit; 85 if(TimerValue >= limit) 86 { 87 irqs[2 * hartId + 1].Set(true); 88 } 89 })); 90 91 registersMap.Add((long)Registers.MTimeCmpHart0Hi + 8 * hartId, new DoubleWordRegister(this).WithValueField(0, 32, writeCallback: (_, value) => 92 { 93 var limit = mTimers[hartId].Compare; 94 limit &= 0xffffffffUL; 95 limit |= (ulong)value << 32; 96 97 irqs[2 * hartId + 1].Set(false); 98 mTimers[hartId].Compare = limit; 99 if(TimerValue >= limit) 100 { 101 irqs[2 * hartId + 1].Set(true); 102 } 103 })); 104 } 105 106 registers = new DoubleWordRegisterCollection(this, registersMap); 107 108 Connections = new ReadOnlyDictionary<int, IGPIO>(irqs); 109 } 110 Reset()111 public void Reset() 112 { 113 registers.Reset(); 114 foreach(var irq in irqs.Values) 115 { 116 irq.Set(false); 117 } 118 foreach(var timer in mTimers) 119 { 120 timer.Reset(); 121 } 122 } 123 ReadDoubleWord(long offset)124 public uint ReadDoubleWord(long offset) 125 { 126 return registers.Read(offset); 127 } 128 WriteDoubleWord(long offset, uint value)129 public void WriteDoubleWord(long offset, uint value) 130 { 131 registers.Write(offset, value); 132 } 133 134 public ulong TimerValue 135 { 136 get 137 { 138 if(machine.SystemBus.TryGetCurrentCPU(out var cpu)) 139 { 140 cpu.SyncTime(); 141 } 142 143 return mTimers[0].Value; 144 } 145 } 146 147 public long Size => 0x10000; 148 149 public IReadOnlyDictionary<int, IGPIO> Connections { get; } 150 AddRegister(long offset, DoubleWordRegister register)151 protected void AddRegister(long offset, DoubleWordRegister register) 152 { 153 registers.AddRegister(offset, register); 154 } 155 156 private readonly DoubleWordRegisterCollection registers; 157 private readonly Dictionary<int, IGPIO> irqs = new Dictionary<int, IGPIO>(); 158 private readonly List<ComparingTimer> mTimers = new List<ComparingTimer>(); 159 private readonly long timerFrequency; 160 private readonly IMachine machine; 161 162 private enum Registers : long 163 { 164 MSipHart0 = 0x0, 165 MTimeCmpHart0Lo = 0x4000, 166 MTimeCmpHart0Hi = 0x4004, 167 MTimeLo = 0xBFF8, 168 MTimeHi = 0xBFFC 169 } 170 } 171 } 172