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