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; 8 using ELFSharp.ELF; 9 using Antmicro.Renode.Core; 10 using Antmicro.Renode.Utilities; 11 using Antmicro.Renode.Logging; 12 using Antmicro.Renode.Utilities.Binding; 13 using Antmicro.Renode.Peripherals.Timers; 14 using Antmicro.Renode.Time; 15 16 namespace Antmicro.Renode.Peripherals.CPU 17 { 18 public class PicoRV32 : RiscV32 19 { PicoRV32(IMachine machine, string cpuType, bool latchedIrqs = true, uint hartId = 0, uint resetVectorAddress = 0x10)20 public PicoRV32(IMachine machine, string cpuType, bool latchedIrqs = true, uint hartId = 0, uint resetVectorAddress = 0x10) : base(machine, cpuType, null, hartId, PrivilegedArchitecture.Priv1_09, Endianess.LittleEndian) 21 { 22 this.latchedIrqs = latchedIrqs; 23 24 // the frequency value is picked at random and not tested 25 internalTimer = new LimitTimer(machine.ClockSource, 1000000, this, nameof(internalTimer), workMode: WorkMode.OneShot, eventEnabled: true); 26 internalTimer.LimitReached += () => 27 { 28 lock(irqLock) 29 { 30 pendingInterrupts |= (1u << TimerInterruptSource); 31 TlibSetReturnRequest(); 32 } 33 }; 34 35 qRegisters = new uint[NumberOfQRegisters]; 36 37 // this will enable handling interrupts locally 38 TlibSetReturnOnException(1); 39 40 this.resetVectorAddress = resetVectorAddress; 41 42 InstallCustomInstruction(pattern: "0000000-----000ss---ddddd0001011", handler: HandleGetqInstruction); 43 InstallCustomInstruction(pattern: "0000001-----sssss---000dd0001011", handler: HandleSetqInstruction); 44 InstallCustomInstruction(pattern: "0000010-----00000---000000001011", handler: HandleRetirqInstruction); 45 InstallCustomInstruction(pattern: "0000011-----sssss---ddddd0001011", handler: HandleMaskirqInstruction); 46 InstallCustomInstruction(pattern: "0000100-----00000---ddddd0001011", handler: HandleWaitirqInstruction); 47 InstallCustomInstruction(pattern: "0000101-----sssss---ddddd0001011", handler: HandleTimerInstruction); 48 49 Reset(); 50 } 51 OnGPIO(int number, bool value)52 public override void OnGPIO(int number, bool value) 53 { 54 this.Log(LogLevel.Noisy, "External interrupt #{0} set to {1}", number, value); 55 lock(irqLock) 56 { 57 if(value) 58 { 59 irqState |= (1u << number); 60 pendingInterrupts |= (1u << number); 61 } 62 else 63 { 64 irqState &= ~(1u << number); 65 if(!latchedIrqs) 66 { 67 pendingInterrupts &= ~(1u << number); 68 } 69 } 70 71 if(IrqIsPending(out var _)) 72 { 73 TlibSetReturnRequest(); 74 } 75 } 76 } 77 Reset()78 public override void Reset() 79 { 80 base.Reset(); 81 82 interruptsMasked = false; 83 pendingInterrupts = 0; 84 irqState = 0; 85 // the cpu starts with all interrutps disabled 86 disabledInterrupts = 0xFFFFFFFF; 87 88 internalTimer.Reset(); 89 } 90 91 // this CPU does not implement Privileged Architecture spec 92 // so those CSRs should not be available at all 93 public new RegisterValue SSTATUS => 0; 94 public new RegisterValue SIE => 0; 95 public new RegisterValue STVEC => 0; 96 public new RegisterValue SSCRATCH => 0; 97 public new RegisterValue SEPC => 0; 98 public new RegisterValue SCAUSE => 0; 99 public new RegisterValue STVAL => 0; 100 public new RegisterValue SIP => 0; 101 102 public new RegisterValue MSTATUS => 0; 103 public new RegisterValue MISA => 0; 104 public new RegisterValue MEDELEG => 0; 105 public new RegisterValue MIDELEG => 0; 106 public new RegisterValue MIE => 0; 107 public new RegisterValue MTVEC => 0; 108 public new RegisterValue MSCRATCH => 0; 109 public new RegisterValue MEPC => 0; 110 public new RegisterValue MCAUSE => 0; 111 public new RegisterValue MTVAL => 0; 112 public new RegisterValue MIP => 0; 113 ExecutionFinished(ExecutionResult result)114 protected override bool ExecutionFinished(ExecutionResult result) 115 { 116 this.Log(LogLevel.Noisy, "PC@0x{1:X}: Execution finished with result: {0}", result, PC.RawValue); 117 118 lock(irqLock) 119 { 120 switch((Result)result) 121 { 122 case Result.IllegalInstruction: 123 case Result.EBreak: 124 case Result.ECall: 125 pendingInterrupts |= (1u << EBreakECallIllegalInstructionInterruptSource); 126 break; 127 128 case Result.LoadAddressMisaligned: 129 case Result.StoreAddressMisaligned: 130 pendingInterrupts |= (1u << UnalignedMemoryAccessInterruptSource); 131 break; 132 133 case (Result)ExecutionResult.Ok: 134 // to avoid warning 135 break; 136 137 default: 138 this.Log(LogLevel.Warning, "Unexpected execution result: {0}", result); 139 break; 140 } 141 142 if(IrqIsPending(out var interruptsToHandle)) 143 { 144 qRegisters[0] = PC; 145 qRegisters[1] = interruptsToHandle; 146 pendingInterrupts &= ~interruptsToHandle; 147 PC = resetVectorAddress; 148 interruptsMasked = true; 149 150 this.Log(LogLevel.Noisy, "Entering interrupt, return address: 0x{0:X}, interrupts: 0x{1:X}", qRegisters[0], qRegisters[1]); 151 } 152 } 153 154 return base.ExecutionFinished(result); 155 } 156 IrqIsPending(out uint interruptsToHandle)157 private bool IrqIsPending(out uint interruptsToHandle) 158 { 159 var pendingExceptions = (pendingInterrupts & ExceptionsMask); 160 if((pendingExceptions != 0) 161 && (interruptsMasked || ((disabledInterrupts & pendingExceptions) != 0))) 162 { 163 // according to the readme: 164 // An illegal instruction or bus error while the illegal instruction or bus error interrupt is disabled will cause the processor to halt. 165 // since we currently have no nice way of halting emulation from random thread, error message must do 166 this.Log(LogLevel.Error, "Illegal instruction / bus error detected, but respective interrupt is disabled or the cpu is currently handling an IRQ"); 167 interruptsToHandle = 0; 168 return false; 169 } 170 171 interruptsToHandle = pendingInterrupts & (~disabledInterrupts); 172 if(interruptsMasked) 173 { 174 interruptsToHandle = 0; 175 } 176 177 return interruptsToHandle != 0; 178 } 179 HandleGetqInstruction(UInt64 opcode)180 private void HandleGetqInstruction(UInt64 opcode) 181 { 182 var rd = (int)BitHelper.GetValue(opcode, 7, 5); 183 var qs = (int)BitHelper.GetValue(opcode, 15, 2); 184 185 if(rd != 0) 186 { 187 X[rd] = qRegisters[qs]; 188 } 189 190 this.Log(LogLevel.Noisy, "PC@0x{3:X}: Handling getq instruction: setting X[{0}] to the value of q[{1}]: 0x{2:X}", rd, qs, qRegisters[qs], PC.RawValue); 191 } 192 HandleSetqInstruction(UInt64 opcode)193 private void HandleSetqInstruction(UInt64 opcode) 194 { 195 var qd = (int)BitHelper.GetValue(opcode, 7, 2); 196 var rs = (int)BitHelper.GetValue(opcode, 15, 5); 197 198 qRegisters[qd] = X[rs]; 199 200 this.Log(LogLevel.Noisy, "PC@0x{3:X}: Handling setq instruction: setting q[{0}] to the value of X[{1}]: 0x{2:X}", qd, rs, X[rs].RawValue, PC.RawValue); 201 } 202 HandleRetirqInstruction(UInt64 opcode)203 private void HandleRetirqInstruction(UInt64 opcode) 204 { 205 lock(irqLock) 206 { 207 var currentPC = PC.RawValue; 208 209 PC = qRegisters[0]; 210 interruptsMasked = false; 211 212 pendingInterrupts |= irqState; 213 if(IrqIsPending(out var _)) 214 { 215 TlibSetReturnRequest(); 216 } 217 218 this.Log(LogLevel.Noisy, "PC@0x{1:X}: Handling retirq instruction: jumping back to 0x{0:X}", qRegisters[0], currentPC); 219 } 220 } 221 HandleMaskirqInstruction(UInt64 opcode)222 private void HandleMaskirqInstruction(UInt64 opcode) 223 { 224 var rd = (int)BitHelper.GetValue(opcode, 7, 5); 225 var rs = (int)BitHelper.GetValue(opcode, 15, 5); 226 227 var newValue = (uint)X[rs].RawValue; 228 var previousValue = disabledInterrupts; 229 230 lock(irqLock) 231 { 232 disabledInterrupts = newValue; 233 if(rd != 0) 234 { 235 X[rd] = previousValue; 236 } 237 238 if(IrqIsPending(out var _)) 239 { 240 TlibSetReturnRequest(); 241 } 242 } 243 244 this.Log(LogLevel.Noisy, "PC@0x{4:X}: Handling maskirq instruction: setting new mask of value 0x{0:X} read from register X[{1}]; old value 0x{2:X} written to X[{3}]", newValue, rs, previousValue, rd, PC.RawValue); 245 } 246 HandleWaitirqInstruction(UInt64 opcode)247 private void HandleWaitirqInstruction(UInt64 opcode) 248 { 249 TlibEnterWfi(); 250 } 251 HandleTimerInstruction(UInt64 opcode)252 private void HandleTimerInstruction(UInt64 opcode) 253 { 254 var rd = (int)BitHelper.GetValue(opcode, 7, 5); 255 var rs = (int)BitHelper.GetValue(opcode, 15, 5); 256 257 var previousValue = (uint)internalTimer.Value; 258 var newValue = X[rs]; 259 if(rd != 0) 260 { 261 X[rd] = previousValue; 262 } 263 internalTimer.Value = newValue; 264 internalTimer.Enabled = (newValue != 0); 265 266 this.Log(LogLevel.Noisy, "Handling timer instruction: setting new value 0x{0:X} read from register X[{1}]; old value 0x{2:X} written to X[{3}]", newValue, rs, previousValue, rd); 267 } 268 269 private bool interruptsMasked; 270 private uint pendingInterrupts; 271 private uint disabledInterrupts; 272 private uint irqState; 273 274 private readonly bool latchedIrqs; 275 private readonly uint resetVectorAddress; 276 private readonly uint[] qRegisters; 277 private readonly LimitTimer internalTimer; 278 private readonly object irqLock = new object(); 279 280 // 649: Field '...' is never assigned to, and will always have its default value null 281 #pragma warning disable 649 282 283 [Import] 284 private Action TlibEnterWfi; 285 286 [Import] 287 private Func<int, int> TlibSetReturnOnException; 288 289 #pragma warning restore 649 290 291 private const uint NumberOfQRegisters = 4; 292 293 private const int TimerInterruptSource = 0; 294 private const int EBreakECallIllegalInstructionInterruptSource = 1; 295 private const int UnalignedMemoryAccessInterruptSource = 2; 296 297 private const uint ExceptionsMask = ((1u << UnalignedMemoryAccessInterruptSource) 298 | (1u << EBreakECallIllegalInstructionInterruptSource)); 299 300 private enum Result 301 { 302 IllegalInstruction = 0x2, 303 EBreak = 0x3, 304 LoadAddressMisaligned = 0x4, 305 StoreAddressMisaligned = 0x6, 306 ECall = 0x8 307 } 308 } 309 } 310