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