1 // 2 // Copyright (c) 2010-2025 Antmicro 3 // Copyright (c) 2011-2015 Realtime Embedded 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 using System; 9 using System.Linq; 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Peripherals.Bus; 12 using Antmicro.Renode.Utilities.Binding; 13 using System.Collections.Generic; 14 using Antmicro.Renode.Time; 15 using Antmicro.Renode.Logging; 16 using Antmicro.Renode.Peripherals.IRQControllers; 17 using Endianess = ELFSharp.ELF.Endianess; 18 19 namespace Antmicro.Renode.Peripherals.CPU 20 { 21 // Changing CPU slot after start is not supported because of InitCPUId call and 22 // because it is hardly ever needed. 23 [GPIO(NumberOfInputs = 3)] 24 public partial class Sparc : TranslationCPU 25 { Sparc(string cpuType, IMachine machine, Endianess endianness = Endianess.BigEndian)26 public Sparc(string cpuType, IMachine machine, Endianess endianness = Endianess.BigEndian): base(cpuType, machine, endianness) 27 { 28 Init(); 29 } 30 31 public override string Architecture { get { return "sparc"; } } 32 33 public override string GDBArchitecture { get { return Architecture; } } 34 35 public override List<GDBFeatureDescriptor> GDBFeatures { get { return new List<GDBFeatureDescriptor>(); } } 36 37 public bool ShutdownAsNop 38 { 39 get => neverWaitForInterrupt; 40 set 41 { 42 neverWaitForInterrupt = value; 43 } 44 } 45 Init()46 private void Init() 47 { 48 49 } 50 Start()51 public override void Start() 52 { 53 InitCPUId(); 54 base.Start(); 55 } 56 OnGPIO(int number, bool value)57 public override void OnGPIO(int number, bool value) 58 { 59 switch(number) 60 { 61 case 0: 62 // Interrupt GPIO set from GaislerIRQMP controller 63 if(isPowerDown) 64 { 65 // Clear state when CPU has been issued a power-down (ASR19) 66 isPowerDown = false; 67 } 68 base.OnGPIO(number, value); 69 break; 70 case 1: 71 // Reset GPIO set from GaislerIRQMP controller 72 this.Log(LogLevel.Noisy, "Sparc Reset IRQ {0}, value {1}", number, value); 73 Reset(); 74 this.Log(LogLevel.Info, "Setting Entry Point value to 0x{0:X}", this.EntryPoint); 75 TlibSetEntryPoint(this.EntryPoint); 76 break; 77 case 2: 78 // Run GPIO set from GaislerIRQMP controller 79 this.Log(LogLevel.Noisy, "Sparc Run IRQ {0}, value {1}", number, value); 80 // Undo halted CPU 81 TlibClearWfi(); 82 if(IsHalted) 83 { 84 IsHalted = false; 85 } 86 if(this.IsStarted) 87 { 88 this.Start(); 89 } 90 break; 91 default: 92 this.Log(LogLevel.Warning, "GPIO index out of range"); 93 break; 94 } 95 } 96 97 private GaislerMIC connectedMIC; 98 99 public GaislerMIC ConnectedMIC 100 { 101 get 102 { 103 if (connectedMIC == null) 104 { 105 var gaislerMics = machine.GetPeripheralsOfType<GaislerMIC>(); 106 foreach (var mic in gaislerMics) 107 { 108 for(var micIndex=0; micIndex < mic.GetNumberOfProcessors(); micIndex++) 109 { 110 var endpoints = mic.GetCurrentCpuIrq(micIndex).Endpoints; 111 for(var i = 0; i < endpoints.Count; ++i) 112 { 113 if(endpoints[i].Receiver == this) 114 { 115 connectedMIC = mic; 116 return connectedMIC; 117 } 118 } 119 } 120 } 121 } 122 return connectedMIC; 123 } 124 } 125 DecodeInterrupt(int number)126 protected override Interrupt DecodeInterrupt(int number) 127 { 128 switch(number) 129 { 130 case 0: 131 return Interrupt.Hard; 132 case 1: 133 return Interrupt.TargetExternal0; 134 case 2: 135 return Interrupt.TargetExternal1; 136 default: 137 throw InvalidInterruptNumberException; 138 } 139 } 140 GetExceptionDescription(ulong exceptionIndex)141 protected override string GetExceptionDescription(ulong exceptionIndex) 142 { 143 return ExceptionDescriptionsMap.TryGetValue(exceptionIndex, out var result) 144 ? result 145 : base.GetExceptionDescription(exceptionIndex); 146 } 147 148 public uint EntryPoint { get; private set; } 149 150 [Export] FindBestInterrupt()151 private int FindBestInterrupt() 152 { 153 if( ConnectedMIC != null ) 154 { 155 if(machine.SystemBus.TryGetCurrentCPU(out var cpu)) 156 { 157 return ConnectedMIC.CPUGetInterrupt((int)cpu.MultiprocessingId); 158 } 159 else 160 { 161 this.Log(LogLevel.Warning, "Find best interrupt - Could not get CPUId."); 162 } 163 } 164 return 0; 165 } 166 167 [Export] AcknowledgeInterrupt(int interruptNumber)168 private void AcknowledgeInterrupt(int interruptNumber) 169 { 170 if( ConnectedMIC != null ) 171 { 172 if(machine.SystemBus.TryGetCurrentCPU(out var cpu)) 173 { 174 ConnectedMIC.CPUAckInterrupt((int)cpu.MultiprocessingId, interruptNumber); 175 } 176 else 177 { 178 this.Log(LogLevel.Warning, "Acknowledge interrupt - Could not get CPUId."); 179 } 180 } 181 } 182 183 [Export] OnCpuHalted()184 private void OnCpuHalted() 185 { 186 IsHalted = true; 187 } 188 189 [Export] OnCpuPowerDown()190 private void OnCpuPowerDown() 191 { 192 isPowerDown = true; 193 this.NoisyLog("CPU has been powered down"); 194 } 195 InitCPUId()196 private void InitCPUId() 197 { 198 if(!cpuIdinitialized) 199 { 200 int cpuid = machine.SystemBus.GetCPUSlot(this); 201 // Only update ASR17 for slave cores 1-15 202 if(cpuid > 0 && cpuid < 16) 203 { 204 TlibSetSlot(cpuid); 205 this.NoisyLog("Current CPUId is {0:X}.", cpuid); 206 } 207 else 208 { 209 this.NoisyLog("Could not set CPUId - value {0:X} is outside of allowed range", cpuid); 210 } 211 // Halt the slave cores, only core 0 starts automatically 212 if(cpuid > 0 && cpuid < 16) 213 { 214 TlibSetWfi(); 215 this.NoisyLog("Halting current CPU - core number {0:X}.", cpuid); 216 } 217 cpuIdinitialized = true; 218 } 219 } 220 AfterPCSet(uint value)221 private void AfterPCSet(uint value) 222 { 223 SetRegisterValue32((int)SparcRegisters.NPC, value + 4); 224 if(!entryPointInitialized) 225 { 226 EntryPoint = value; 227 entryPointInitialized = true; 228 this.Log(LogLevel.Info, "Using PC value as Entry Point value : 0x{0:X}", EntryPoint); 229 } 230 } 231 232 private bool cpuIdinitialized = false; 233 private bool entryPointInitialized; 234 private bool isPowerDown; 235 236 // 649: Field '...' is never assigned to, and will always have its default value null 237 #pragma warning disable 649 238 239 [Import] 240 private Action<int> TlibSetSlot; 241 242 [Import] 243 private Action<uint> TlibSetEntryPoint; 244 245 [Import] 246 private Action TlibClearWfi; 247 248 [Import] 249 private Action TlibSetWfi; 250 251 #pragma warning restore 649 252 253 private readonly Dictionary<ulong, string> ExceptionDescriptionsMap = new Dictionary<ulong, string> 254 { 255 {0x01, "Instruction access exception"}, 256 {0x02, "Illegal instruction"}, 257 {0x03, "Privileged instruction"}, 258 {0x04, "FP disabled"}, 259 {0x05, "Window overflow"}, 260 {0x06, "Window underflow"}, 261 {0x07, "Memory address not aligned"}, 262 {0x08, "FP exception"}, 263 {0x09, "Data access exception"}, 264 {0x0A, "Tag overflow"}, 265 {0x0B, "Watchpoint detected"}, 266 {0x20, "R register access error"}, 267 {0x21, "Instruction access error"}, 268 {0x24, "CP disabled"}, 269 {0x25, "Unimplemented FLUSH"}, 270 {0x28, "CP Exception"}, 271 {0x29, "Data access error"}, 272 {0x2A, "Division by zero"}, 273 {0x2B, "Data store error"}, 274 {0x2C, "Data access MMU miss"}, 275 {0x3C, "Instruction access MMU miss"} 276 }; 277 } 278 } 279 280