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