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.Collections.ObjectModel;
10 using System.Linq;
11 using Antmicro.Renode.Core;
12 using Antmicro.Renode.Core.Extensions;
13 using Antmicro.Renode.Core.Structure.Registers;
14 using Antmicro.Renode.Exceptions;
15 using Antmicro.Renode.Logging;
16 using Antmicro.Renode.Peripherals.Bus;
17 using Antmicro.Renode.Peripherals.CPU;
18 using Antmicro.Renode.Utilities;
19 
20 namespace Antmicro.Renode.Peripherals.IRQControllers
21 {
22     public class CoreLocalInterruptController : IBytePeripheral, IDoubleWordPeripheral, IIndirectCSRPeripheral, IProvidesRegisterCollection<DoubleWordRegisterCollection>, IProvidesRegisterCollection<ByteRegisterCollection>, IGPIOReceiver
23     {
CoreLocalInterruptController(IMachine machine, BaseRiscV cpu, uint numberOfInterrupts = 4096, ulong machineLevelBits = 8, ulong supervisorLevelBits = 8, ulong modeBits = 2, ulong interruptInputControlBits = 8, bool configurationHasNvbits = true )24         public CoreLocalInterruptController(IMachine machine, BaseRiscV cpu, uint numberOfInterrupts = 4096,
25             ulong machineLevelBits = 8, // MNLBITS
26             ulong supervisorLevelBits = 8, // SNLBITS
27             ulong modeBits = 2, // NMBITS
28             ulong interruptInputControlBits = 8, // CLICINTCTLBITS
29             // add the nvbits field to the configuration register to match the legacy layout from the 2022-09-27 specification,
30             // as found in some hardware implementations
31             bool configurationHasNvbits = true
32             )
33         {
34             this.machine = machine;
35 
36             if(machineLevelBits > 8)
37             {
38                 throw new ConstructionException($"Invalid {nameof(machineLevelBits)}: provided {machineLevelBits} is larger than the maximum 8.");
39             }
40 
41             if(supervisorLevelBits > 8)
42             {
43                 throw new ConstructionException($"Invalid {nameof(supervisorLevelBits)}: provided {supervisorLevelBits} is larger than the maximum 8.");
44             }
45 
46             if(modeBits > 2)
47             {
48                 throw new ConstructionException($"Invalid {nameof(modeBits)}: provided {modeBits} is larger than the maximum 2.");
49             }
50 
51             if(interruptInputControlBits > MaxInterruptInputControlBits)
52             {
53                 throw new ConstructionException($"Invalid {nameof(interruptInputControlBits)}: provided {interruptInputControlBits} is larger than the maximum {MaxInterruptInputControlBits}.");
54             }
55 
56             if(numberOfInterrupts < 2 || numberOfInterrupts > 4096)
57             {
58                 throw new ConstructionException($"Invalid {nameof(numberOfInterrupts)}: provided {numberOfInterrupts} but must be between 2 and 4096.");
59             }
60 
61             this.cpu = cpu;
62             this.numberOfInterrupts = numberOfInterrupts;
63             // clicinttrig functionality is not implemented
64             this.numberOfTriggers = 0;
65             defaultMachineLevelBits = machineLevelBits;
66             defaultSupervisorLevelBits = supervisorLevelBits;
67             defaultModeBits = modeBits;
68             this.configurationHasNvbits = configurationHasNvbits;
69             this.interruptInputControlBits = interruptInputControlBits;
70             unimplementedInputControlBits = (int)MaxInterruptInputControlBits - (int)interruptInputControlBits;
71 
72             ByteRegisters = new ByteRegisterCollection(this);
73             DoubleWordRegisters = new DoubleWordRegisterCollection(this);
74 
75             interruptPending = new IFlagRegisterField[numberOfInterrupts];
76             interruptEnable = new IFlagRegisterField[numberOfInterrupts];
77             vectored = new IFlagRegisterField[numberOfInterrupts];
78             edgeTriggered = new IFlagRegisterField[numberOfInterrupts];
79             negative = new IFlagRegisterField[numberOfInterrupts];
80             mode = new IValueRegisterField[numberOfInterrupts];
81             inputControl = new ulong[numberOfInterrupts];
82 
83             cpu.RegisterLocalInterruptController(this);
84 
85             DefineRegisters();
86             Reset();
87         }
88 
Reset()89         public void Reset()
90         {
91             DoubleWordRegisters.Reset();
92             ByteRegisters.Reset();
93             machineLevelBits.Value = defaultMachineLevelBits;
94             if(!configurationHasNvbits)
95             {
96                 supervisorLevelBits.Value = defaultSupervisorLevelBits;
97             }
98             modeBits.Value = defaultModeBits;
99             bestInterrupt = NoInterrupt;
100             acknowledgedInterrupt = NoInterrupt;
101             cpu.ClicPresentInterrupt(NoInterrupt, false, MinLevel, PrivilegeLevel.User);
102         }
103 
ReadByte(long offset)104         public byte ReadByte(long offset)
105         {
106             return ByteRegisters.Read(offset);
107         }
108 
WriteByte(long offset, byte value)109         public void WriteByte(long offset, byte value)
110         {
111             ByteRegisters.Write(offset, value);
112         }
113 
ReadDoubleWord(long offset)114         public uint ReadDoubleWord(long offset)
115         {
116             if(DoubleWordRegisters.TryRead(offset, out var result))
117             {
118                 return result;
119             }
120             return this.ReadDoubleWordUsingByte(offset);
121         }
122 
WriteDoubleWord(long offset, uint value)123         public void WriteDoubleWord(long offset, uint value)
124         {
125             if(DoubleWordRegisters.TryWrite(offset, value))
126             {
127                 return;
128             }
129             this.WriteDoubleWordUsingByte(offset, value);
130         }
131 
ReadIndirectCSR(uint iselect, uint ireg)132         public uint ReadIndirectCSR(uint iselect, uint ireg)
133         {
134             // iselect is the offset from the beginning of this peripheral's indirect CSR range
135             // ireg is the 0-based index of the iregX CSR (ireg - 0, ireg2 - 1, ...)
136             if(iselect < InterruptControlAttribute || iselect > ClicConfiguration || (ireg != 0 && ireg != 1))
137             {
138                 LogUnhandledIndirectCSRRead(iselect, ireg);
139                 return 0x0;
140             }
141 
142             if(iselect < InterruptPendingEnable)
143             {
144                 var start = (iselect - InterruptControlAttribute) * 4 + (ireg == 0 ? 2 : 3); // ireg: control, ireg 2: attr
145                 return ReadByte(start)
146                     | ((uint)ReadByte(start + 4) << 8)
147                     | ((uint)ReadByte(start + 8) << 16)
148                     | ((uint)ReadByte(start + 12) << 24);
149             }
150 
151             if(iselect < InterruptTrigger)
152             {
153                 var start = (iselect - InterruptPendingEnable) * 32;
154                 return BitHelper.GetValueFromBitsArray(
155                     ((ireg == 0) ? interruptPending : interruptEnable)
156                         .Skip((int)start)
157                         .Take(32)
158                         .Select(r => r.Value)
159                 );
160             }
161 
162             if(ireg == 1)
163             {
164                 LogUnhandledIndirectCSRRead(iselect, ireg);
165                 return 0x0;
166             }
167 
168             if(iselect < ClicConfiguration)
169             {
170                 return ReadDoubleWord(iselect - InterruptTrigger + (long)Register.InterruptTrigger0);
171             }
172 
173             this.WarningLog("Register reserved (iselect 0x{0:x}, ireg {1})", iselect, ireg);
174             return 0x0;
175         }
176 
WriteIndirectCSR(uint iselect, uint ireg, uint value)177         public void WriteIndirectCSR(uint iselect, uint ireg, uint value)
178         {
179             // iselect is the offset from the beginning of this peripheral's indirect CSR range
180             // ireg is the 0-based index of the iregX CSR (ireg - 0, ireg2 - 1, ...)
181             if(iselect < InterruptControlAttribute || iselect > ClicConfiguration || (ireg != 0 && ireg != 1))
182             {
183                 LogUnhandledIndirectCSRWrite(iselect, ireg);
184                 return;
185             }
186 
187             if(iselect < InterruptPendingEnable)
188             {
189                 var start = (iselect - InterruptControlAttribute) * 4 + (ireg == 0 ? 2 : 3);
190                 WriteByte(start, (byte)value);
191                 WriteByte(start + 4, (byte)(value >> 8));
192                 WriteByte(start + 8, (byte)(value >> 16));
193                 WriteByte(start + 16, (byte)(value >> 24));
194                 return;
195             }
196 
197             if(iselect < InterruptTrigger)
198             {
199                 var flags = (ireg == 0) ? interruptPending : interruptEnable;
200                 var i = (iselect - InterruptPendingEnable) * 32;
201                 foreach(var b in BitHelper.GetBits(value))
202                 {
203                     flags[i++].Value = b;
204                     // clicinttrig not implemented
205                 }
206                 return;
207             }
208 
209             if(ireg == 1)
210             {
211                 LogUnhandledIndirectCSRWrite(iselect, ireg);
212                 return;
213             }
214 
215             if(iselect < ClicConfiguration)
216             {
217                 WriteDoubleWord(iselect - InterruptTrigger + (long)Register.InterruptTrigger0, value);
218                 return;
219             }
220 
221             this.WarningLog("Register reserved (iselect 0x{0:x}, ireg {1}), value 0x{2:X}", iselect, ireg, value);
222             return;
223         }
224 
OnGPIO(int number, bool value)225         public void OnGPIO(int number, bool value)
226         {
227             if(number < 0 || number > numberOfInterrupts)
228             {
229                 this.ErrorLog("Invalid GPIO number {0}: supported range is 0 to {1}", number, numberOfInterrupts);
230                 return;
231             }
232 
233             if(edgeTriggered[number].Value)
234             {
235                 interruptPending[number].Value |= value ^ negative[number].Value;
236             }
237             else
238             {
239                 interruptPending[number].Value = value ^ negative[number].Value;
240             }
241             bool output = UpdateInterrupt();
242             this.DebugLog("Incoming interrupt #{0} set to {1}, enabled={2} edgeTriggered={3} negative={4} -> output={5}",
243                 number, value, interruptEnable[number].Value, edgeTriggered[number].Value, negative[number].Value, output);
244         }
245 
ClearEdgeInterrupt()246         public void ClearEdgeInterrupt()
247         {
248             if(bestInterrupt == NoInterrupt || !edgeTriggered[bestInterrupt].Value)
249             {
250                 return;
251             }
252             interruptPending[bestInterrupt].Value = false;
253             UpdateInterrupt();
254         }
255 
AcknowledgeInterrupt()256         public void AcknowledgeInterrupt()
257         {
258             acknowledgedInterrupt = bestInterrupt;
259             this.DebugLog("Acknowledged interrupt #{0}", acknowledgedInterrupt);
260         }
261 
GetInterruptPrivilege(int number)262         private PrivilegeLevel GetInterruptPrivilege(int number)
263         {
264             if(number == NoInterrupt)
265             {
266                 return PrivilegeLevel.User;
267             }
268 
269             var itMode = mode[number].Value;
270             switch(modeBits.Value)
271             {
272                 case 0:
273                     return PrivilegeLevel.Machine;
274                 case 1:
275                     return (itMode & 0b10) == 0 ? PrivilegeLevel.Supervisor : PrivilegeLevel.Machine;
276                 case 2:
277                     return (PrivilegeLevel)itMode; // matching representation
278                 default: // the reserved value 3 will be remapped on write, so this is unreachable
279                     throw new InvalidOperationException($"Encountered reserved interrupt privilege {modeBits.Value}, should not happen");
280             }
281         }
282 
GetInterruptLevel(int number)283         private int GetInterruptLevel(int number)
284         {
285             if(number == NoInterrupt)
286             {
287                 return MinLevel; // MinLevel - normal execution, not in ISR
288             }
289 
290             var privilege = GetInterruptPrivilege(number);
291             var levelBits = (int)(privilege == PrivilegeLevel.Machine || configurationHasNvbits ? machineLevelBits.Value : supervisorLevelBits.Value);
292             var otherBits = (int)MaxInterruptInputControlBits - levelBits;
293             // left-justify and append 1s to fill the unused bits on the right
294             return (int)(BitHelper.GetValue(inputControl[number], otherBits, levelBits) << otherBits) | ((1 << otherBits) - 1);
295         }
296 
GetInterruptPriority(int number)297         private int GetInterruptPriority(int number)
298         {
299             if(number == NoInterrupt)
300             {
301                 return -1; // below lowest valid priority
302             }
303 
304             var privilege = GetInterruptPrivilege(number);
305             var levelBits = (int)(privilege == PrivilegeLevel.Machine || configurationHasNvbits ? machineLevelBits.Value : supervisorLevelBits.Value);
306             var priorityBits = (int)interruptInputControlBits - levelBits;
307             if(priorityBits <= 0)
308             {
309                 // No priority bits are available. All interrupts will have the same priority.
310                 // The spec doesn't define what the value should be; the below is consistent with the behavior
311                 // when priority bits are available. We assume all the unimplemented bits are 1
312                 // and remaining bits are 0. This should not matter anyway, as all priorities are the same in this case.
313                 return (1 << unimplementedInputControlBits) - 1;
314             }
315             return (int)(BitHelper.GetValue(inputControl[number], unimplementedInputControlBits, priorityBits) << unimplementedInputControlBits) | ((1 << unimplementedInputControlBits) - 1);
316         }
317 
UpdateInterrupt()318         private bool UpdateInterrupt()
319         {
320             // Clear the previous best interrupt if it is not enabled or pending anymore
321             if(bestInterrupt != NoInterrupt)
322             {
323                 if(!(interruptEnable[bestInterrupt].Value && interruptPending[bestInterrupt].Value))
324                 {
325                     bestInterrupt = NoInterrupt;
326                 }
327             }
328             var bestPrivilege = GetInterruptPrivilege(bestInterrupt);
329             var bestLevel = GetInterruptLevel(bestInterrupt);
330             var bestPriority = GetInterruptPriority(bestInterrupt);
331             for(int i = 0; i < numberOfInterrupts; ++i)
332             {
333                 if(!interruptEnable[i].Value || !interruptPending[i].Value)
334                 {
335                     continue;
336                 }
337                 var currentPrivilege = GetInterruptPrivilege(i);
338                 var currentLevel = GetInterruptLevel(i);
339                 var currentPriority = GetInterruptPriority(i);
340                 // If the privilege or level is higher, take it as the best interrupt. If it only differs in priority, only take it if the core hasn't
341                 // already started handling the previous best interrupt, as priority does not cause preemption.
342                 if(currentPrivilege > bestPrivilege ||
343                    currentLevel > bestLevel ||
344                    (currentPrivilege == bestPrivilege && currentLevel == bestLevel && currentPriority > bestPriority && acknowledgedInterrupt == NoInterrupt))
345                 {
346                     bestInterrupt = i;
347                     bestLevel = currentLevel;
348                     bestPriority = currentPriority;
349                     bestPrivilege = currentPrivilege;
350                 }
351             }
352             if(bestInterrupt != NoInterrupt)
353             {
354                 var bestVectored = vectored[bestInterrupt].Value;
355                 cpu.ClicPresentInterrupt(bestInterrupt, bestVectored, bestLevel, bestPrivilege);
356                 this.DebugLog("Presenting interrupt #{0} to core, vectored {1} level {2} priority {3} privilege {4}", bestInterrupt, bestVectored, bestLevel, bestPriority, bestPrivilege);
357                 return true;
358             }
359             else
360             {
361                 cpu.ClicPresentInterrupt(NoInterrupt, false, MinLevel, PrivilegeLevel.User);
362                 this.DebugLog("Clearing current interrupt state - no interrupt pending");
363                 acknowledgedInterrupt = NoInterrupt;
364                 return false;
365             }
366         }
367 
LogUnhandledIndirectCSRRead(uint iselect, uint ireg)368         private void LogUnhandledIndirectCSRRead(uint iselect, uint ireg)
369         {
370             this.WarningLog("Unhandled read from register via indirect CSR access (iselect 0x{0:x}, ireg {1})", iselect, ireg);
371         }
372 
LogUnhandledIndirectCSRWrite(uint iselect, uint ireg)373         private void LogUnhandledIndirectCSRWrite(uint iselect, uint ireg)
374         {
375             this.WarningLog("Unhandled write to register via indirect CSR access (iselect 0x{0:x}, ireg {1})", iselect, ireg);
376         }
377 
DefineRegisters()378         protected void DefineRegisters()
379         {
380             var this_dword = this as IProvidesRegisterCollection<DoubleWordRegisterCollection>;
381             var this_byte = this as IProvidesRegisterCollection<ByteRegisterCollection>;
382             if(configurationHasNvbits)
383             {
384                 Register.Configuration.Define8(this_byte)
385                     .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => true, name: "nvbits")
386                     .WithValueField(1, 4, out machineLevelBits, name: "mnlbits")
387                     .WithValueField(5, 2, out modeBits, name: "nmbits", changeCallback: (_, value) =>
388                     {
389                         if(value == 3)
390                         {
391                             this.WarningLog("A value of 3 for nmbits is reserved, forcing to 2");
392                             modeBits.Value = 2;
393                         }
394                     })
395                     .WithReservedBits(7, 1);
396             }
397             else
398             {
399                 Register.Configuration.Define32(this_dword)
400                     .WithValueField(0, 4, out machineLevelBits, name: "mnlbits")
401                     .WithValueField(4, 2, out modeBits, name: "nmbits", changeCallback: (_, value) =>
402                     {
403                         if(value == 3)
404                         {
405                             this.WarningLog("A value of 3 for nmbits is reserved, forcing to 2");
406                             modeBits.Value = 2;
407                         }
408                     })
409                     .WithReservedBits(6, 10)
410                     .WithValueField(16, 4, out supervisorLevelBits, name: "snlbits")
411                     .WithReservedBits(20, 4)
412                     .WithTag("unlbits", 24, 4)
413                     .WithReservedBits(28, 4)
414                 ;
415             }
416 
417             Register.Information.Define32(this_dword)
418                 .WithValueField(0, 13, FieldMode.Read, valueProviderCallback: _ => numberOfInterrupts, name: "num_interrupt")
419                 .WithValueField(13, 8, FieldMode.Read, valueProviderCallback: _ => 0, name: "version")
420                 .WithValueField(21, 4, FieldMode.Read, valueProviderCallback: _ => interruptInputControlBits, name: "CLICINTCTLBITS")
421                 .WithValueField(25, 6, FieldMode.Read, valueProviderCallback: _ => numberOfTriggers, name: "num_trigger")
422                 .WithReservedBits(31, 1)
423             ;
424 
425             Register.InterruptTrigger0.Define32Many(this_dword, numberOfTriggers, (register, index) =>
426             {
427                 register
428                     .WithTag("interrupt_number", 0, 13)
429                     .WithReservedBits(13, 17)
430                     .WithTaggedFlag("nxti_enable", 30)
431                     .WithTaggedFlag("enable", 31)
432                 ;
433             });
434 
435             Register.InterruptPending0.Define8Many(this_byte, numberOfInterrupts, (register, index) =>
436             {
437                 register
438                     .WithFlag(0, out interruptPending[index], name: "pending", changeCallback: (oldValue, value) =>
439                     {
440                         if(!edgeTriggered[index].Value)
441                         {
442                             this.WarningLog("Changing the pending bit of level-triggered interrupt #{0} ({1} -> {2}) is not allowed", index, oldValue, value);
443                             interruptPending[index].Value = oldValue;
444                             return;
445                         }
446                         UpdateInterrupt();
447                         this.DebugLog("Set interrupt #{0} pending {1}", index, interruptPending[index].Value);
448                     })
449                     .WithReservedBits(1, 7);
450                 ;
451             }, 4);
452 
453             Register.InterruptEnable0.Define8Many(this_byte, numberOfInterrupts, (register, index) =>
454             {
455                 register
456                     .WithFlag(0, out interruptEnable[index], name: "enable", changeCallback: (_, value) =>
457                     {
458                         UpdateInterrupt();
459                         this.DebugLog("Set interrupt #{0} enabled {1}", index, value);
460                     })
461                     .WithReservedBits(1, 7)
462                 ;
463             }, 4);
464 
465             Register.InterruptAttribute0.Define8Many(this_byte, numberOfInterrupts, (register, index) =>
466             {
467                 register
468                     .WithFlag(0, out vectored[index], name: "shv")
469                     .WithFlag(1, out edgeTriggered[index], name: "edge_triggered") // 0=level, 1=edge
470                     .WithFlag(2, out negative[index], name: "negative") // 0=positive (rising), 1=negative (falling)
471                     .WithReservedBits(3, 3)
472                     .WithValueField(6, 2, out mode[index], name: "mode")
473                     .WithChangeCallback((_, __) =>
474                     {
475                         UpdateInterrupt();
476                         this.DebugLog("Set interrupt #{0} edge-triggered {1} negative {2}", index, edgeTriggered[index].Value, negative[index].Value);
477                     })
478                 ;
479             }, 4, resetValue: (byte)PrivilegeLevel.Machine << 6);
480 
481             Register.InterruptInputControl0.Define8Many(this_byte, numberOfInterrupts, (register, index) =>
482             {
483                 register
484                     .WithValueField(0, (int)MaxInterruptInputControlBits, name: "input_control",
485                         writeCallback: (_, val) =>
486                         {
487                             inputControl[index] = (ulong)((int)val | ((1 << unimplementedInputControlBits) - 1));
488                         },
489                         valueProviderCallback: _ =>
490                         {
491                             return inputControl[index];
492                         }
493                     )
494                     .WithChangeCallback((_, __) => UpdateInterrupt());
495                 ;
496             }, 4);
497         }
498 
499         DoubleWordRegisterCollection IProvidesRegisterCollection<DoubleWordRegisterCollection>.RegistersCollection => DoubleWordRegisters;
500         ByteRegisterCollection IProvidesRegisterCollection<ByteRegisterCollection>.RegistersCollection => ByteRegisters;
501 
502         private DoubleWordRegisterCollection DoubleWordRegisters { get; }
503         private ByteRegisterCollection ByteRegisters { get; }
504 
505         private int bestInterrupt;
506         private int acknowledgedInterrupt;
507 
508         private readonly IFlagRegisterField[] interruptPending;
509         private readonly IFlagRegisterField[] interruptEnable;
510         private readonly IFlagRegisterField[] vectored;
511         private readonly IFlagRegisterField[] edgeTriggered;
512         private readonly IFlagRegisterField[] negative;
513         private readonly IValueRegisterField[] mode;
514         private readonly ulong[] inputControl;
515         private IValueRegisterField machineLevelBits;
516         private IValueRegisterField supervisorLevelBits;
517         private IValueRegisterField modeBits;
518 
519         private readonly IMachine machine;
520         private readonly BaseRiscV cpu;
521         private readonly uint numberOfInterrupts;
522         private readonly uint numberOfTriggers;
523         private readonly ulong defaultMachineLevelBits;
524         private readonly ulong defaultSupervisorLevelBits;
525         private readonly ulong defaultModeBits;
526         private readonly ulong interruptInputControlBits; // CLICINTCTLBITS
527         private readonly int unimplementedInputControlBits;
528         private readonly bool configurationHasNvbits;
529 
530         private const ulong MaxInterruptInputControlBits = 8;
531         private const int MinLevel = 0;
532         private const int NoInterrupt = -1;
533 
534         // Relative offsets for the various register groups in the indirect CSR space
535         private const uint InterruptControlAttribute = 0x000; // - 0x3FF
536         private const uint InterruptPendingEnable    = 0x400; // - 0x47F
537         private const uint InterruptTrigger          = 0x480; // - 0x49F
538         private const uint ClicConfiguration         = 0x4A0;
539 
540         private enum Register
541         {
542             Configuration           = 0x0000,
543             Information             = 0x0004,
544             Reserved0               = 0x0008, // - 0x003F
545             InterruptTrigger0       = 0x0040, // - 0x00BC
546             Reserved1               = 0x00C0, // - 0x07FF
547             Custom                  = 0x0800, // - 0x0FFF
548             InterruptPending0       = 0x1000, // 0x1000 + 4 * i
549             InterruptEnable0        = 0x1001, // 0x1001 + 4 * i
550             InterruptAttribute0     = 0x1002, // 0x1002 + 4 * i
551             InterruptInputControl0  = 0x1003, // 0x1003 + 4 * i
552         }
553     }
554 }
555